1. add(E e):
// 向清單中追加一個元素。
// size:目前清單中元素的數量。
public boolean add(E e) {
// size+1 表示最小容量。最小容量 == 目前清單中元素個數+1。
ensureCapacityInternal(size + 1); // Increments modCount!!
// 到這裡已經擴容結束了。将新元素追加到數組。
elementData[size++] = e;
// 添加成功,傳回 true。
return true;
}
看下擴容函數:
ensureCapacityInternal(size + 1)
private void ensureCapacityInternal(int minCapacity) {
// 1.先計算出所需的最小容量。
// 2.确認最小容量,并執行擴容。
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
(1)計算所需的最小容量。如果是向空清單中 add 元素,最小容量取
預設容量(10)
和
待添加的元素個數
中較大的那個。如果是向非空清單中 add 元素,最小容量直接取:
目前清單中元素的個數 + 待添加的元素的個數
。
// 計算需要的最小容量。minCapacity == 目前數組元素個數 + 要添加的元素的個數。
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
// DEFAULT_CAPACITY = 10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 向空數組中添加元素,比如第一次 add 元素時才會觸發
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 傳回 預設容量 和 minCapacity 中最大的。
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 向非空數組 add 時,直接傳回 minCapacity。
return minCapacity;
}
(2.1)确認是否擴容。隻有當 計算所需要的最小容量 大于 目前數組的長度時,才擴容。
// minCapacity 是計算出來的最小容量。
private void ensureExplicitCapacity(int minCapacity) {
// 這個是校驗并發用的。
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// 當需要的最小容量 > 目前數組的長度時, 執行擴容。
grow(minCapacity);
}
(2.2) 執行擴容。
// minCapacity 是所需的細小容量。
private void grow(int minCapacity) {
// 目前數組的長度。
int oldCapacity = elementData.length;
// 擴容到目前數組長度的 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果擴容後的容量還小于所需的最小容量,那就以計算需要的為準(再将容量擴大)。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 再判斷,如果新容量是個很大的數子,快逼近 int 的極限的了,但還沒有溢出,
// 那就直接将新容量擴大到 int 的極限。
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);
}
// 極大的擴容
private static int hugeCapacity(int minCapacity) {
// 如果容量超 int 範圍了,報異常。
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 三段式,确定大容量的準确值。
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2. addAll(Collection<? extends E> c)
// 向清單中追加一個list。
public boolean addAll(Collection<? extends E> c) {
// 拿出 list 中元素儲存到數組中。
Object[] a = c.toArray();
// 擷取追加的元素個數。
int numNew = a.length;
// 給原本的 elementData 擴容。
ensureCapacityInternal(size + numNew); // Increments modCount
// 将 a 數組中的元素從 0 開始,依次複制到 elementData 的 size 位置開始,并往後推
// 總共複制 numNew 個。
System.arraycopy(a, 0, elementData, size, numNew);
// 更新清單長度。
size += numNew;
return numNew != 0;
}
3. add(int index, E element)
// 在指定索引出插入元素。
public void add(int index, E element) {
// 檢查是否越界。
rangeCheckForAdd(index);
// 擴容。
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将 index 位置以及後面的元素,向後移動 1 為。
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 在指定位置插入元素。
elementData[index] = element;
// 清單中元素個數 + 1。
size++;
}
4. addAll(int index, Collection<? extends E> c)
// 在指定索引出插入一個集合。
public boolean addAll(int index, Collection<? extends E> c) {
// 檢查索引是否越界。
rangeCheckForAdd(index);
// 集合裡的元素儲存到數組中。
Object[] a = c.toArray();
// 數組長度。
int numNew = a.length;
// 擴容。
ensureCapacityInternal(size + numNew); // Increments modCount
// 原數組中要向後移動的元素的個數。
int numMoved = size - index;
// 如果向後移動的元素的個數大于 0。
if (numMoved > 0)
// index 位置開始向後,總共 numMoved 元素,均向後移動 numNew 的距離。
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 将 a 中的元素從 elementData 的 index 位置開始,其次複制到 elementData 中。
System.arraycopy(a, 0, elementData, index, numNew);
// 清單中總元素個數更新。
size += numNew;
return numNew != 0;
}
5. ArrayList() 無參構造函數。
private static final int DEFAULT_CAPACITY = 10;
public ArrayList() {
// 建立空數組,此時 ArrayList 對象預設的 capacity = 10。
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
6. 小問題
public class MyArrayListAdd {
public static void main(String[] args){
ArrayList<String> demo = new ArrayList<>();
demo.add("diego");
demo.add("amos");
}
}
三行代碼,每執行一行,清單容量和數組的數組的長度是分别是多少?
預設容量:10 ,空數組長度是:0
向空清單中 add 元素,需求容量是 1,預設容量是 10 ,取較大的,是以計算的最小容量是 10。因為 最小容量大于目前數組長度,要擴容,最終确認的容量也是 10,是以建立長度為 10 的數組,最後向裡面追加一個元素。
容量:10,數組長度:10,但裡面隻有一個元素。
向非空清單中 add 元素,需求容量是 2,是以計算出來的最小容量直接是 2。因為計算出來的最小容量小于目前數組的長度。不需要擴容。直接在向數組中追加新元素。
容量:2,數組長度 10,裡面有兩個元素。
7. Vector 和 ArrayList 的差別是什麼?
Vector 是線程安全版的 ArrayList。
對比一下 add 方法的源碼就能看清楚了
// Vector.java
// synchronized 加鎖
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
// ArrayList.java
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}