ArrayList 源碼分析

開篇

ArrayList 是 Java 裏面經常使用的一種集合,它的本質就是一個動態數組, 不只知足了數組有序存儲的特性,還彌補了數組沒法動態擴容的缺陷。  	
ArrayList 的實現原理基於它內部維護了一個 Object 數組,當 ArrayList 容器中的數量達到必定限度時,會從新 new 一個更大的數組,並將原先數組中的數據拷貝過去。

源碼分析(基於 jdk1.8.0_191 版本)

ArrayList 中比較經常使用的方法就是添加元素 add() 和 獲取元素 get(),其中add() 方法比較複雜。因此分析的重心就放在了這個方法上,下面就開始一步一步解剖。

幾個重要屬性

  • DEFAULT_CAPACITY 默認容量
  • elementData ArrayList 內部維護的數組
  • EMPTY_ELEMENTDATA 一個空數組,被全部的空實例共享
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA 也是一個空數組, 與上面的區分開來。以便肯定當添加了第一個成員以後擴充多大
  • size ArrayList 存放元素的個數

構造方法

ArrayList 裏面一共有三個構造方法, 一個無參構造 和 兩個有參構造,重點介紹兩個關鍵的構造方法。
  1. 指定容量的有參構造算法

    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);
        }
    }
  2. 無參構造數組

    public ArrayList() {
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     }

add() 方法

public boolean add(E e) {
    // 1⃣️ 確認容器中的容量, 擴容也就是在這一步進行的
    ensureCapacityInternal(size + 1);  // Increments modCount!!
     // 下面的代碼就沒什麼好說的了,把元素放到指定索引上
    elementData[size++] = e;
    return true;
}

ensureCapacityInternal() 方法

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的位置。
相關文章
相關標籤/搜索