Arraylist的扩容机制


点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。

本文在公众号文章已同步,还有各种一线大厂面试原题、我的学习系列笔记。

arraylist每次添加元素时都会检查是否需要扩容:arraylist第一次添加元素时,赋予arraylist默认容量10,再往里面添加元素(所以arraylist默认容量10并不是初始化的时候赋予的,而是无参构造第一次添加元素的时候赋予的);以后每次添加元素前先检查当前元素个数是否已经达到容量上限,若是则先以1.5倍*原容量上限进行扩容再添加元素,如下:

  • arraylist中的初始变量
//当首次创建arraylist是无参构造,即没有指定初始容量时,赋予该默认数组给arraylist
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//当首次创建arraylist是有参构造,即指定了初始容量,但初始容量指定为0时,赋予该默认数组给arraylist
private static final Object[] EMPTY_ELEMENTDATA = {};
//用于arraylist中实际存放元素的数组,注意此变量是transient修饰的,不参与序列化
transient Object[] elementData;
//数组元素的实际个数,默认0;不同于elementData.length,elementData.length是数组长度
private int size;
  • arraylist的三个构造函数
(1)默认无参构造:
public ArrayList() {
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
(2)指定了长度的有参构造
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];//指定长度>0,直接赋予指定长度的数组
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;//指定长度为0,赋予默认的长度为0的数组
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}
(3)指定了数组元素的有参构造
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
  • arraylist添加元素,两种方法
//把元素添加到数组尾部
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 检查是否需要扩容,size为当前实际元素个数,size+1为添加元素后需要的实际最小空间;第一次添加元素时size为0,传入1
    elementData[size++] = e;//检查完再往里面添加元素
    return true;
}
//把元素添加到数组特定的下标处
public void add(int index, E element) {
    rangeCheckForAdd(index);
    ensureCapacityInternal(size + 1);   // 检查是否需要扩容
    System.arraycopy(elementData, index, elementData, index + 1,size - index);//检查完先拷贝元素:把元素后移
    elementData[index] = element;//再添加元素
    size++;
}
  • 检查是否需要扩容
private void ensureCapacityInternal(int minCapacity){  
    //calculateCapacity()获取添加元素后所需的最小数组容量
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//获取添加元素后所需的最小数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //调用的无参构造初始化arraylist,且是第一次添加元素,返回10,即设置‘添加元素后所需的最小数组容量’为10:minCapacity=1,elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA,DEFAULT_CAPACITY为10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //其他情况:调用有参构造初始化arraylist且第一次添加元素或非第一次添加元素,则‘添加元素后所需的最小数组容量’为添加元素后实际元素个数
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;//用于快速失败机制
    //当前数组长度=elementData.length < minCapacity='添加元素后所需的最小数组容量',则需要扩容
    //(1)无参构造第一次添加元素时:10>0,需扩容,传入10;(2)无参构造添加元素后所需的最小数组容量为11时:11>10,需扩容,传入11;...
    if (minCapacity - elementData.length > 0) 
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); //扩容1.5倍:oldCapacity>>1即oldCapacity/2=0.5*oldCapacity
    if (newCapacity - minCapacity < 0)
    //无参构造第一次添加元素时会走这一步:oldCapacity=newCapacity=0,minCapacity=10
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        //扩容后新容量newCapacity>MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8时
        newCapacity = hugeCapacity(minCapacity);
    //(1)无参构造第一次添加元素时elementData为空数组,newCapacity=10
    elementData = Arrays.copyOf(elementData, newCapacity);
}

OK,如果文章哪里有错误或不足,欢迎各位留言。
创作不易,各位的「三连」是二少创作的最大动力!我们下期见!


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM