只記錄目前為止關注的。JDK1.8
一、基礎屬性
1.1 內部參數
//空存儲實例。直接new ArrayList()便是以該空數組作為實例
private static final Object[] EMPTY_ELEMENTDATA = {};
//默認容量大小,在由空實例進行首次擴容時,擴到到該長度。
//實際使用中,並未實際存在Capacity這個參數,需要擴容時直接根據舊數組的length進行擴容
private static final int DEFAULT_CAPACITY = 10;
//實際存儲數組
transient Object[] elementData;
//存儲元素個數
private int size;
//該字段用於迭代器的fail-fast機制【參考另一篇文章】
protected transient int modCount = 0;
1.2 三個重載構造方法
//構建一個空實例,elementData指向容量為0的空數組
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//以給定容量初始化存儲數組
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);
}
}
//以集合初始化創建列表
//步驟:1. 調用toArray(),將給定集合轉成數組(以ArrayList為例,toArray()返回的是實際存在元素的那部分數組,即[0,size))
//2. 讓size直接等於給定集合的數組長度
//3. 判斷size如果為0則直接創建空存儲實例,否則使用Arrays.copyOf將給定集合數組復制一份(淺拷貝),作為存儲數組
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 的 toArray()源碼,復制[0,size)部分返回
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
二、操作及策略
2.1 動態擴容
擴容策略:當數組全滿了才擴容,新長度=舊長度*1.5
動態擴容有兩個入口:供用戶調用的顯式擴容ensureCapacity()
和添加元素時的隱式擴容ensureCapacityInternal()
,不過均是調用ensureExplicitCapacity()
來根據傳入所需容量值決定是否擴容,最終實際擴容操作在grow()
方法中。
//顯式擴容入口方法
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//隱式擴容入口方法
//其中參數 minCapacity 值為 size+1,由add方法調用傳入
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//添加方法
public boolean add(E e) {
// 傳入最小所需容量為size+1
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//計算所需容量,實則不需要計算
//只是單純判斷當前是否是空實例,為空就話返回 "默認容量"與minCapacity之間的較大值,不為空直接返回minCapacity
//參數 minCapacity 只有兩種情況:
// 1. 隱式擴容時,如add()傳入,其值為 size+1
// 2. 顯式擴容,用戶指定
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//在此處,根據最小所需容量來判斷是否實際進行擴容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //若已經占滿,才進行擴容
grow(minCapacity);
}
//實際擴容方法
//可以看到擴容策略為:length + length*0.5(右移1位縮小1倍)
//然后調用Arrays.copyOf淺復制
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //舊長度+舊長度*2
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);
}
2.2 刪除操作
ArrayList中有兩個remove方法public E remove(int index)
和public boolean remove(Object o)
源碼實現得其實挺簡單,本來還在猶豫有沒有必要把remove方法記錄下來,但發現有個點值得注意以下:
注意點:JDK對於數組內元素統一移動操作,偏好使用System.arraycopy(ary, srcPos, ary, destPos, len)
來移動
//通過給定下標來刪除元素,並返回刪除元素
public E remove(int index) {
rangeCheck(index); // 檢查下標范圍
modCount++;
E oldValue = elementData(index); // 合理的話,先獲取該元素(等會兒用於返回)
int numMoved = size - index - 1; // 計算待移動元素個數(刪除元素后,后面的元素要統一往前挪補上)
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved); // 使用System.arraycopyl來移動數組元素
elementData[--size] = null; // clear to let GC do its work // 刪除后size-1,並將先前最后一個元素置null
return oldValue; //返回刪除元素
}
// 判斷下標是否合理(<size),否則拋出異常
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 刪除首個與給定元素相等的元素,元素判等使用equal方法
// 成功返回true,失敗返回false
public boolean remove(Object o) {
if (o == null) { // 可以刪除null節點
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index); // 參考下面方法注釋
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) { // 從頭開始一個個檢查,使用equal判等
fastRemove(index);
return true;
}
}
return false;
}
/*
* 官方注釋說得很清楚了:刪除元素,但跳過邊界檢查且不返回刪除元素。(就是remove(index)的縮略版
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}