ArrayList 是 Java 裏面經常使用的一種集合,它的本質就是一個動態數組, 不只知足了數組有序存儲的特性,還彌補了數組沒法動態擴容的缺陷。 ArrayList 的實現原理基於它內部維護了一個 Object 數組,當 ArrayList 容器中的數量達到必定限度時,會從新 new 一個更大的數組,並將原先數組中的數據拷貝過去。
ArrayList 中比較經常使用的方法就是添加元素 add() 和 獲取元素 get(),其中add() 方法比較複雜。因此分析的重心就放在了這個方法上,下面就開始一步一步解剖。
ArrayList 裏面一共有三個構造方法, 一個無參構造 和 兩個有參構造,重點介紹兩個關鍵的構造方法。
指定容量的有參構造算法
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); } }
無參構造數組
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
public boolean add(E e) { // 1⃣️ 確認容器中的容量, 擴容也就是在這一步進行的 ensureCapacityInternal(size + 1); // Increments modCount!! // 下面的代碼就沒什麼好說的了,把元素放到指定索引上 elementData[size++] = e; return true; }
private void ensureCapacityInternal(int minCapacity) { // 2⃣️ 下面分析 calculateCapacity ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { // 若是經過 ArrayList 建立的實例, 那麼在添加第一個元素時, 會獲得一個容量爲 10 的 ArrayList 實例 // 第二次添加元素時,就不會走這一步了。 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // DEFAULT_CAPACITY = 10 return Math.max(DEFAULT_CAPACITY, minCapacity); } // 若是經過 ArrayList(initCapacity), 指定了初始容量,哪怕是 0 就直接返回。 return minCapacity; } // ③ 下面會拿一個空的容器來解釋 也就是 經過new ArrayList()建立容器實例 private void ensureExplicitCapacity(int minCapacity) { modCount++;// 這個變量能夠先無論, 他表示 容器中 size 變化的次數。 // 當添加第 1 個元素時, elementData.length的長度 爲 0, minCapacity 爲10,因此最終會建立一個容量爲 10 的 ArrayList // 當添加第 2 個元素時, minCapacity爲 1 ,elementData.length 爲10, 無需擴容。 // ...... // 當添加第 11 個元素時,minCapacity爲 11, elementData.length 爲10, 會擴容1.5倍 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); // 拷貝數組,並添加新元素 elementData = Arrays.copyOf(elementData, newCapacity); }
1. 當經過 ArrayList() 構造一個空集合,初始長度是爲0的,第 1 次添加元素,會建立一個長度爲10的數組,並將該元素賦值到數組的第一個位置。 2. 第 2 次添加元素,集合不爲空,並且因爲集合的長度 size+1 是小於數組的長度 10,因此直接添加元素到數組的第二個位置,不用擴容。 3. 第 11 次添加元素,此時 size+1 = 11,而數組長度是10,這時候建立一個長度爲10+10*0.5 = 15 的數組(擴容1.5倍), 而後將原數組元素引用拷貝到新數組。並將第 11 次添加的元素賦值到新數組下標爲10的位置。