天天看點

ArrayList源碼:add 方法7. Vector 和 ArrayList 的差別是什麼?

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;
}