重要屬性
//初次擴容(size為0)時的默認容量
private static final int DEFAULT_CAPACITY = 10;
//存在常量池的零值
private static final Object[] EMPTY_ELEMENTDATA = {};
//實際存儲對象的數組,文檔管他叫array buffer
//容量就是指elementData的長度
private transient Object[] elementData;
//數組最大值是2147483639,不是2147483647的原因是
//有些虛擬機在創建數組時會有頭部信息
//超過這個值會報OutOfMemoryError
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
初始擴容
- 如果容量從0到1,比如調用add(E e),會將數組elementData變成{obj,null,null,null,null,null,null,null,null,null}
- 如果容量從0到12(超過了DEFAULT_CAPACITY ),比如調用addAll(Collection<? extends E>c)添加一個長度為12的數組,那么elementData的長度就是12
超過10后的擴容
如果容量超過10,再次擴容時比較,至少需要的容量和1.5當前容量,取大值。至少需要的容量指實際元素的個數(size的值)。比如當前容量15,實際元素12,需要一次性添加4個元素,那么至少需要的容量是16,1.5當前容量是22,容量將變為22。
代碼實現
//以下三個函數用來確認是否需要擴容
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != EMPTY_ELEMENTDATA)
// any size if real element table
? 0
// larger than default for empty table. It's already supposed to be
// at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//計算擴容值的函數
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
//分配容量的函數
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
trimToSize方法
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}
由于擴容后會產生很多填充的null,這個方法可以講這些null都去掉以節省內存,做法是調用一次copyOf方法創建一個新數組再把原來的拷貝進去。
總結
注意到copyOf方法會重新創建一個新的newLength長度的Object[],并調用native方法來將原數組復制到新的Object[]里,這個動作開銷還是很大的。這也就是數組擴容邏輯的意義:盡量減少調用這個copyOf方法,從而提高效率。