ArrayList的擴容機制

ArrayList的擴容機制:數組

當向ArrayList中添加元素的時候,ArrayList的存儲容量若是知足新元素的容量要求,則直接存儲;ArrayList的存儲容量若是不知足新元素的容量要求,ArrayList會加強自身的存儲能力,以達到存儲新元素的要求。this

由於不一樣的JDK版本的擴容機制可能有差別,下面以JDK1.8爲例說明
1、構造方法spa

    public ArrayList(int initialCapacity) {
        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.
     * ArrayList():並非在初始化ArrayList的時候就設置初始容量爲空,而是在添加第一個元素時,將初始容量設置爲10(DEFAULT_CAPACITY)。
     * 在jdk1.6中,無參構造方法的初始容量爲10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

2、主要方法
1. 參數說明.net

    //默認的初始容量
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 當使用構造方法ArrayList(int initialCapacity) 時,若initialCapacity=0,則將ArrayList初始化爲EMPTY_ELEMENTDATA
     * 或使用ArrayList(Collection<? extends E> c) ,collection爲空時
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 當使用構造方法ArrayList() 時,將ArrayList初始化爲空
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * elementData是存儲ArrayList元素的數組。
     * ArrayList的容量也就是數組elementData的長度。 
     * 添加第一個元素時,任何elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA(即數組爲空,elementData = {})的狀況,ArrayList的容量都將擴展爲DEFAULT_CAPACITY。
     */
    transient Object[] elementData; // non-private to simplify nested class access


    //The size of the ArrayList (the number of elements it contains).
    private int size;

2. add(E e)方法3d

    public boolean add(E e) {
        //判斷是否能夠容納e,若能,則直接添加在末尾;若不能,則進行擴容,而後再把e添加在末尾
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //將e添加到數組末尾
        elementData[size++] = e;
        return true;
    }

每次在add()一個元素時,arraylist都須要對這個list的容量進行一個判斷。經過ensureCapacityInternal()方法確保當前ArrayList維護的數組具備存儲新元素的能力,通過處理以後將元素存儲在數組elementData的尾部
3. ensureCapacityInternal方法code

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

        ensureExplicitCapacity(minCapacity);
    }

ensureCapacityInternal判斷ArrayList默認的元素存儲數據是否爲空,爲空則設置最小要求的存儲能力爲必要存儲的元素和默認存儲元素個數的兩個數據之間的最大值,而後調用ensureExplicitCapacity方法實現這種最低要求的存儲能力
4. ensureExplicitCapacity方法對象

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

若ArrayList已有的存儲能力知足最低存儲要求,則返回add直接添加元素;若是最低要求的存儲能力>ArrayList已有的存儲能力,這就表示ArrayList的存儲能力不足,所以須要調用 grow();方法進行擴容
5. grow()方法blog

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); //設置新的存儲能力爲原來的1.5倍
        if (newCapacity - minCapacity < 0) //擴容以後仍小於最低存儲要求minCapacity
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0) //private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

當ArrayList擴容的時候,首先會設置新的存儲能力爲原來的1.5倍,若是擴容以後仍小於必要存儲要求minCapacity,則取值爲minCapacity。
若新的存儲能力大於MAX_ARRAY_SIZE,則取值爲Integer.MAX_VALUE
在grow方法中,肯定ArrayList擴容後的新存儲能力後,調用的Arrays.copyof()方法進行對原數組的複製,再經過調用System.arraycopy()方法(native修飾)進行復制,達到擴容的目的
6. hugeCapacity方法ci

    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中的當前容量已經爲Integer.MAX_VALUE,仍向ArrayList中添加元素時,拋出異常element

public class ArrayOrLinked2{
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        
        arrayList.add(1);
        for(int i = 0; i < Integer.MAX_VALUE; i++) //超過ArrayList的最大容量
            arrayList.add(i);
    }
}

ArrayList擴容的例子:
ArrayList至關於在沒指定initialCapacity時就是會使用延遲分配對象數組空間,當第一次插入元素時才分配10(默認)個對象空間。

public class ArrayListTest2 {
    
    public static void main(String[] args) {
    
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        
        arrayList.add(1);
        for(int i = 0; i < 19; i++)
            arrayList.add(i);
    }
}

假若有20個數據須要添加,那麼會分別在第一次的時候,將ArrayList的容量變爲10 (以下圖一);以後擴容會按照1.5倍增加。也就是當添加第11個數據的時候,Arraylist繼續擴容變爲10*1.5=15(以下圖二);當添加第16個數據時,繼續擴容變爲15 * 1.5 =22個。


總結
在JDK1.8中,若是經過無參構造的話,初始數組容量爲0,當真正對數組進行添加時(即添加第一個元素時),才真正分配容量,默認分配容量爲10;當容量不足時(容量爲size,添加第size+1個元素時),先判斷按照1.5倍(位運算)的比例擴容可否知足最低容量要求,若能,則以1.5倍擴容,不然以最低容量要求進行擴容。

執行add(E e)方法時,先判斷ArrayList當前容量是否知足size+1的容量;
在判斷是否知足size+1的容量時,先判斷ArrayList是否爲空,若爲空,則先初始化ArrayList初始容量爲10,再判斷初始容量是否知足最低容量要求;若不爲空,則直接判斷當前容量是否知足最低容量要求;
若知足最低容量要求,則直接添加;若不知足,則先擴容,再添加。

ArrayList的最大容量爲Integer.MAX_VALUE
參考:
https://blog.csdn.net/yang1464657625/article/details/59109133
http://www.javashuo.com/article/p-rreicrno-bx.html

相關文章
相關標籤/搜索