關於ArrayList的擴容機制


關於ArrayList的擴容機制


 

        ArrayList作為List接口常用的一個實現類,其底層數據接口由數組實現,可以保證O(1) 復雜度的隨機查找, 在增刪效率上不如LinkedList,但是在查詢效率較高,相對同是數組實現的Vector,並不能保證線程安全,所以多適用於單線程環境。

由於底層是有數組實現,因為數組的長度需要初始化定義,並不能自動進行長度增加,所以ArrayList有對應的擴容機制,當增加元素時,會判斷是否需要擴容,下面看源碼:

首先認識ArrayList的幾個重要變量:

    /*序列化ID*/
    private static final long serialVersionUID = 8683452581122892189L;
    /*默認的數組容量,第一次進行添加操作時,如果ArrayList為空或者添加元素后的數組長度小於等於10,會將該值作為數組的初始長度*/
    private static final int DEFAULT_CAPACITY = 10;
    /*一個空的對象數組,在需要將elementData數組置空時,會將該對象賦予elementData*/
    private static final Object[] EMPTY_ELEMENTDATA = {};
    /*默認的空對象數組,在ArrayList構造時,默認將該對象給elementData*/
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    /*ArrayList內部用於存儲元素的數組,transient關鍵字修飾,不使用默認序列化*/
    transient Object[] elementData; // non-private to simplify nested class access
    /*數組的長度,是ArrayList中實際元素的個數,不是數組的容量*/
    private int size;

 

在ArrayList的尾部插入和其他位置雖然是不同方法,但是都使用到了ensureCapacityInternal()方法確保數組內部容量,在每次添加操作中都會使用該方法進行容量判斷,之后,才會將增加的元素添加到數組中,下面以尾部插入為例;
   /**
     * 增加數據元素到集合得末尾
     * 
     */
    public boolean add(E e) {
        /*判斷是否擴容,如果原來的元素個數是size,那么增加一個元素之后的元素個數為size + 1,所以需要的最小容量就為size + 1*/
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

 ensureCapacityInternal()將判斷委托給ensureExplicitCapacity()處理

    /*獲取數組最小容量*/
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        /*如果elementData為空,且minCapacity <= 10,都會以DEFAULT_CAPACITY作為最小容量*/
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureCapacityInternal(int minCapacity) {
        /*ensureCapacityInternal方法委托給ensureExplicitCapacity方法*/
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        /*如果minCapacity大於elementData的長度,使用grow方法進行擴容*/
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

 下面就是擴容的實現方法grow方法:

    /*擴容方法*/
    private void grow(int minCapacity) {
        /*原有數組容量*/
        int oldCapacity = elementData.length;
        /*新的數組容量,下面位運算相當於newCapacity = oldCapacity * 1.5 向下取整*/
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        /*如果新的數組容量小於需要的最小容量,即假設新的數組容量是15,最小需要16的容量,則會將16賦予newCapacity*/
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        /*變量MAX_ARRAY_SIZE = 2147483639 [0x7ffffff7],如果擴容后的新容量大於這個值則會使用hugeCapacity方法
         * 判斷最小容量minCapacity是否大於MAX_ARRAY_SIZE,如果需要最小容量的也大於MAX_ARRAY_SIZE,則會以
         * Integer.MAX_VALUE = 2147483647 [0x7fffffff]的值最為數組的最大容量,如果沒有則會以MAX_ARRAY_SIZE最為最大容量
* MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,為什么用MAX_ARRAY_SIZE ,源碼的中的說法是一些虛擬機中會對數組保留一些標題字段
* 使用Integer.MAX_VALUE會造成內存溢出錯誤 *
*/ if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); /*確定數組最終的容量newCapacity之后,將原有ArrayList的元素全部拷貝到一個新的ArrayList中*/ elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { /*如果minCapacity小於0,則拋出內存溢出錯誤*/ if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }

 ArrayList的擴容機制還是相對容易理解的,就是在第一個添加元素時,創建一個長度為10的數組,之后隨着元素的增加,以1.5倍原數組的長度創建一個新數組,即10, 15, 22, 33,。。這樣序列建立,將原來的元素拷貝到新數組之中,如果數組長度達到上限,則會以

MAX_ARRAY_SIZE 或者 Integer.MAX_VALUE作為最大長度,而多余的元素就會被舍棄掉。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM