1. 程式人生 > >ArrayList資料結構與原理分析

ArrayList資料結構與原理分析

ArrayList

ArrayList實現於List,RandomAccess介面,可以插入空資料,也支援隨機訪問

ArrayList相當於動態資料,最重要的引數分別是:elementData陣列,以及size大小在其呼叫add方法的時候:

public boolean add(E e){
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
  • 先進行擴容校驗ensureCapacityInternal(size + 1)
  • 將插入的值放在elementData的尾部並將size+1
private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
  • 先判斷ArrayList預設的元素儲存是否為空,若為空的話則取預設大小和想要初始化大小兩個數中的最大值然後再呼叫 ensureExplicitCapacity(minCapacity)
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  • 如果說初始化長度>elementData陣列的長度,將會觸發ArrayList的擴容機制然後呼叫grow()方法
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);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  • 當ArrayList擴容的時候,首先會設定新的儲存能力為原來的1.5倍
  • 如果擴容之後還是不能滿足要求則MAX_ARRAY_SIZE比較,求取最大值,
    如果MAX_ARRAY_SIZE大小的能力還是不能滿足則通過hugeCapacity()方法獲取ArrayList能允許的最大值
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
  • 從hugeCapacity方法看出,ArrayList最大的儲存能力:儲存元素的個數為整型的範圍。
    確定ArrayList擴容之後最新的可儲存元素個數時,呼叫
    elementData = Arrays.copyOf(elementData, newCapacity);
    實現elementData陣列的擴容,整個流程就是ArrayList的自動擴容機制工作流程
如果是呼叫add(index index,E e)向指定位置新增元素的話
public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //複製,向後移動
        System.arraycopy(elementData, index, elementData, index + 1,
        size - index);
        elementData[index] = element;
        size++;
    }
  • 可以看到第一步依然是進行擴容校驗
  • 接著對陣列進行復制,將element插入index位置上,並將index位置後面的元素依次向後移動一個位置

可以發現,ArrayList擴容的時候要做大量的操作,其中還包括一些呼叫native方法,就不可避免的需要做一些IO操作,所以在初始化ArrayList的時候最好給一個初始值的大小,這樣可以避免後面增加元素的時候做頻繁的擴容。