ArrayList的扩容
前言
ArrayList的扩容会由于ArrayList的构造函数选取的不同有很多种情况,所以我们本文的主要顺序是这样的:先了解ArrayList的构造函数,然后了解ArrayList的两种重载的add方法,以来展示其扩容原理。
ArrayList的两种构造函数
1.默认的无参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2.有参构造
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
另外还有一种有参构造将不在这里介绍了,就是参数是另一种集合的。
两种add方法
1.在尾部的加法
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
2.在任意位置的加法
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)//注释1
elementData = grow();//注释2
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
不难发现第一中加法内部就是调用的第二种加法,接下来我们对第二种加法详细分析一下。
注释1的部分是扩容条件,即当size==elementData.length的时候便扩容,注意:
- size 指的是该容器中存放对象的数量;
- elementData.length指的是ArrayList底层实现的数组的长度。
接下来我们主要来看看grow()函数的内部:
private Object[] grow() {
return grow(size + 1);
}
grow()的另一种重载,参数为size+1,并且形参命名为minCapacity,我的理解是扩容最小应该满足的容量(因为ArrayList的扩容并不是每次扩容一位的,所以这个是最小的):
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 ||//注释3
elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,//注释4
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {//注释5
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
注释5的情况比较简单,先介绍注释5吧,
注释5:一般我们在List容器刚刚初始化并且我们使用的是ArrayList的无参构造器来构造ArrayList容器时(这种情况经常发生),还没有使用的时候都是先执行这一步,可见在这一步要满足两个条件:1,原来的底层数组长度为0(没有加入过元素或者全删了);2,使用的是无参构造。
此时我们返回新的底层数组来实现扩容,在此时DEFAULT_CAPACITY=10,minCapacity=1,所以此时底层的数组长度为10。
注释3:当我们使用的不是默认无参构造来初始化或者在这个add操作之前elementData不为空,就进行到下面的注释4,此时进入到ArraysSupport.newLength()函数中。
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// assert oldLength >= 0
// assert minGrowth > 0
int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
if (newLength - MAX_ARRAY_LENGTH <= 0) {
return newLength;
}
return hugeLength(oldLength, minGrowth);
}
可见,这个函数的返回值就是一个数组,其长度为第一个数组的长度,加上第二个参数与第三个参数中较小的那个。
综述
ArrayList每次扩容为1.5倍。如果按1.5倍扩容后仍不能大于minCapacity=size+1,则扩容到minCapacity,我认为这种情况只会发生在oldElementData长度只有0和1的时候才会发生。