超詳細的ArrayList擴容過程(配合原始碼詳解)

loopyhz發表於2024-11-19

首先,在呼叫add方法的時候 ,會去呼叫 ensureCapacityInternal 方法,傳入一個引數 minCapacity 大小是size + 1,也就是現在我們需要的陣列的最小的大小。
在ensureCapacityInternal 方法中,先判斷一下elementdata是不是初始空陣列
是的話就把minCapacity變更為預設容量 也就是10, 和傳進來的minCapacity的最大值
之後呼叫 ensureExplicitCapacity 方法,把現在的minCapacity傳進去

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

第二步:
在這個ensureExplicitCapacity 方法中,會去判斷一下傳進來的minCapacity和當前elementdata的大小,如果需要的這個最小容量已經超過了當前陣列的長度,就會去呼叫grow方法,也就是擴容的核心方法,並且把這個minCapacity傳進去

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

第三步:
進入grow方法後,先去獲取現在的elementdata的長度,並且先行擴容到1.5倍(這個>>1是位運算)。擴容後的容量記為 newCapacity ,用這個newCapacity 去和minCapacity也就是我們需要的最小的大小進行比較,還是達不到的話就讓newCapacity=minCapacity。接著還要處理一下當前的newCapacity超過MAX_ARRAY_SIZE,也就是ArrayList允許的陣列最大值的情況(一般是Integer.MAX_VALUE - 8)。呼叫hugeCapacity來進行處理,小於0的話就拋異常,超過最大值了就取最大值。最後呼叫Arrays.copyOf()方法。

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 擴容到原來的 1.5 倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity; // 若新容量仍不足,則取所需的最小容量
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity); // 檢查是否超過最大允許大小
    elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError(); // 超出 int 最大值時丟擲異常
    return (minCapacity > MAX_ARRAY_SIZE) ?
           Integer.MAX_VALUE : // 如果實際需求超大,則直接取 Integer.MAX_VALUE
           MAX_ARRAY_SIZE;     // 否則取最大允許的陣列大小
}

最後在這個Arrays.copyOf()方法中會建立一個新的陣列,大小就是我們上一步得到並且傳入的newCapacity。接著把舊陣列中的元素複製到新陣列中。

public static <T> T[] copyOf(T[] original, int newLength) {
    T[] copy = (T[]) Array.newInstance(original.getClass().getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    return copy;
}

相關文章