ArrayList作爲java中常用的集合類型有哪些特點需要我們瞭解呢?本文將基於jdk1.8源碼來一步步列出ArrayList有哪些需要令人注意的要點。
1 RandomAccess隨機訪問接口
ArrayList繼承實現關係如下圖:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
從該圖我們可以看出,ArrayList實現了RandmoAccess接口,RandmoAcess接口實現如下:
public interface RandomAccess {
}
RandmoAccess接口中實際上並無任何實現,該接口只是表示實現了該接口的類能夠提供快速訪問功能。
2 底層是數組類型
ArrayList實現了RandmoAccess接口代表能夠進行快速訪問,而ArrayList的快速訪問功能實際上是靠數組實現的,下面是ArrayList中最重要的兩個屬性:
//ArrayList實際存數據的地方
transient Object[] elementData;
//ArrayList的大小
private int size;
下面我們來看ArrayList的默認構造方法:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
默認構造方法會將elementData屬性賦爲DEFAULTCAPACITY_EMPTY_ELEMENTDATA,其中DEFAULTCAPACITY_EMPTY_ELEMENTDATA屬性是一個空數組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
接下來我們看ArrayList中的add和get方法:
public boolean add(E e) {
//確認是否擴容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public E get(int index) {
//檢查是否越界
rangeCheck(index);
return elementData(index);
}
可以看出Arraylist底層是通過數組來操作的。
3 惰性初始化
ArrayList使用默認構造器創建類時創建的是一個空的數組,那麼ArrayList是如何能夠使用add方法存數據的呢?我們可以詳細看看add方法中的ensureCapacityInternal方法源碼:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY常量數值是10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
//記錄修改次數
modCount++;
if (minCapacity - elementData.length > 0)
//擴容方法
grow(minCapacity);
}
從以上源碼可以看出當ArrayList爲一個空數組時,會賦予一個默認的擴容大小10,然後再進擴容方法裏擴容ArrayList,因此ArrayList使用默認構造器初始化時,不會立馬初始化數組大小,而是等待調用add方法後纔會進行初始化,且初始化大小爲10。
4 位運算1.5倍擴容
我們可以查看ArrayList的grow方法來看看arrayList實際是如何擴容的:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//位運算1.5倍擴容
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//判斷minCapacity是否超出最大整數值
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
以上代碼分爲如下幾個步驟:
- 首先用位運算對原數組進行1.5倍擴容得到新的擴容值newCapacity
- 再用新的擴容值newCapacity與實際傳來的所需擴容值minCapacity進行比較,若newCapacity比minCapacity大則用newCapacity,反之則用minCapacity並判斷minCapacity是否合法,這樣得出的值爲一個最終擴容值
- 最後用Arrays.copyOf方法對最終選出的擴容值進行擴容