Java容器原始碼學習--ArrayList原始碼分析

Sierra、發表於2021-12-26

ArrayList實現了List介面,它的底層資料結構是陣列,因此獲取容器中任意元素值的時間複雜度為O(1),新增或刪除元素的時間複雜度為O(N)。每一個ArrayList例項都有一個capacity變數,capacity是ArrayList用於儲存元素的容器大小,當有新元素新增到容器時,capacity會自動擴容,當新增元素時,容器會計算需要擴容的大小,減少了記憶體重新分配的次數。需要注意的時,ArrayList是非執行緒安全的容器,在多執行緒環境下容易出現執行緒安全問題。

原始碼

成員變數

private static final int DEFAULT_CAPACITY = 10; // 預設的容量是10

private static final Object[] EMPTY_ELEMENTDATA = {}; //初始化容量為0時的空陣列

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //初始化沒有指定容量

transient Object[] elementData; //儲存元素的陣列

private int size; //容器中元素的數量

構造方法

public ArrayList(int initialCapacity) {
       //如果初始化指定容量 > 0,直接建立一個陣列
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //預設的陣列容量大小是10
    }

新增元素


public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }

private void add(E e, Object[] elementData, int s) {
        //容器元素數量等於陣列的長度觸發擴容條件
        if (s == elementData.length)
          //呼叫grow方法進行擴容
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

//擴容邏輯,先計算出需要擴容的大小,再把原陣列元素拷貝到擴容後的陣列
private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }

    private Object[] grow() {
        return grow(size + 1);
    }
//計算擴容後的陣列長度
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // preconditions not checked because of inlining
        // assert oldLength >= 0
        // assert minGrowth > 0

        int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
        if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
            return prefLength;
        } else {
            // put code cold in a separate method
            return hugeLength(oldLength, minGrowth);
        }
    }

通過原始碼可知,新增元素前,先判斷當前容器大小與陣列長度,決定是否要擴容,否則直接新增到容器中。擴容需要計算出擴容後新陣列的長度,新陣列長度是原陣列的1.5倍大小。

在指定位置新增元素

public void add(int index, E element) {
//先判斷插入位置是否合法
        rangeCheckForAdd(index);
//記錄容器被修改的次數
        modCount++;
        final int s;
        Object[] elementData;
//擴容條件
        if ((s = size) == (elementData = this.elementData).length)
            elementData = grow();
//將後面的元素後移,騰出位置
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        elementData[index] = element;
        size = s + 1;
    }

相關文章