与扩容有关的成员变量:
// 数组默认容量
private static final int DEFAULT_CAPACITY = 10;
// 空数组,代表new ArrayList(0)
private static final Object[] EMPTY_ELEMENTDATA = {};
// 默认空数组,代表 new ArrayList();
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存储元素的数组
transient Object[] elementData;
// 当前数组包含的元素个数
private int size;
// 数组最大元素个数
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ArrayList构造方法:
// 无参构造:对应默认空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 有参构造:根据参数创建相应大小的数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) { // 创建大小为initialCapacity的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { // 大小为0, 用空数组替代
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
// 有参构造:根据参数创建相应大小的数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) { // 创建大小为initialCapacity的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { // 大小为0, 用空数组替代
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
扩容的起点:从add(E e)方法开始
public boolean add(E e) {
// size + 1 表示扩容后数组需要的最小容量,此时size = 0
ensureCapacityInternal(size + 1);
// 扩容后在对应位置赋值,元素个数+1
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// calculateCapacity方法用于计算真正的最小容量(下面再解释)
// ensureExplicitCapacity方法判断是否需要扩容
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果是用无参构造得到的数组 返回默认容量大小,也就是10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 否则容量不变,还是 size + 1
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
// 修改次数
modCount++;
// 最小容量大于当前数组长度才需要扩容
if (minCapacity - elementData.length > 0)
// 扩容的核心方法
grow(minCapacity);
}
private void grow(int minCapacity) {
// 旧容量,也就是当前的数组长度
int oldCapacity = elementData.length;
// 扩容后的新容量,约等于旧容量的1.5倍,因为有旧容量可能是奇数
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 扩容后的新容量还是小于最小容量,则还是使用最小容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新容量超过默认的最大数组容量,新容量 = Integer.MAX_VALUE
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 复制当前所有元素到扩容后的新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
根据整个扩容流程可以得出扩容分为如下三种情况:
- 如果使用无参构造方法创建ArrayList:添加第一个元素的时候数组长度扩容到10。之后直到添加第11个元素时开始正常扩容(原容量的1.5倍)
- 如果使用有参构造方法创建ArrayList,且参数为0:添加第一个元素的时候则开始正常扩容。
- 如果使用有参构造方法创建ArrayList,且参数大于0:直到数组满后,再添加元素时则开始正常扩容
补充:grow()中为什么是if (newCapacity - minCapacity < 0)而不是if (newCapacity < minCapacity)?
假设是 if (newCapacity < minCapacity),该语句为true时,是在数组容量非常小(0,1,2)的情况下,扩容后还没有当前容量大,因此还不如直接使用最小容量(当前容量+1)作为扩容后的容量。但是如果此时数组容量处于临界值,1.5倍扩容后溢出变成了一个大负数,该语句还是为true,因此还是把最小容量最为扩容后的新容量,那么由于这时元素数量已经很多了,再把所有元素复制到新数组上效率就比较低。
假设是if (newCapacity - minCapacity < 0),还是在数组容量处于临界值的情况下,这时就是扩容后溢出得到的大负数 + (-最小容量),此时(-最小容量)也是个大负数。两数相加再次溢出得到个正数,因此不会执行newCapacity = minCapacity。这是扩容后的容量则由hugeCapacity()方法得到:如果最小容量大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),新容量则为Integer.MAX_VALUE,否则为MAX_ARRAY_SIZE。这就避免了临界值之后的每次扩容变成当前容量+1而造成的复制元素所带来的巨大开销,而是直接扩容为MAX_ARRAY_SIZE。