Java-ArrayList擴容機制


參考鏈接

ArrayList簡介

ArrayList實現了List接口,它是一個可調整大小的數組,可以用來存放各種形式的數據。它是線程非安全的,按照插入的順序來存儲數據。

ArrayList的主要成員變量:

private static final int DEFAULT_CAPACITY = 10;//數組默認初始容量
 
private static final Object[] EMPTY_ELEMENTDATA = {};//定義一個空的數組實例以供其他需要用到空數組的地方調用 
 
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//定義一個空數組,跟前面的區別就是這個空數組是用來判斷ArrayList第一添加數據的時候要擴容多少。默認的構造器情況下返回這個空數組 
 
transient Object[] elementData;//數據存的地方它的容量就是這個數組的長度,同時只要是使用默認構造器(DEFAULTCAPACITY_EMPTY_ELEMENTDATA )第一次添加數據的時候容量擴容為DEFAULT_CAPACITY = 10 
 
private int size;//當前數組的長度

構造方法

  • ArrayList() 構造一個初始容量為10的空列表。
  • ArrayList(Collection<? extends E> c)
    構造一個包含指定集合的元素的列表,按照它們由集合的迭代器返回的順序。
  • ArrayList(int initialCapacity) 構造具有指定初始容量的空列表。
    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;
        }
    }
 
    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);
        }   

擴容機制

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

ensureCapacityInternal方法

ArrayList擴容的核心從ensureCapacityInternal方法說起。可以看到前面介紹成員變量的提到的ArrayList有兩個默認的空數組:
DEFAULTCAPACITY_EMPTY_ELEMENTDATA:是用來使用默認構造方法時候返回的空數組。如果第一次添加數據的話那么數組擴容長度為DEFAULT_CAPACITY=10。
EMPTY_ELEMENTDATA:出現在需要用到空數組的地方,其中一處就是使用自定義初始容量構造方法時候如果你指定初始容量為0的時候就會返回。

    //判斷當前數組是否是默認構造方法生成的空數組,如果是的話minCapacity=10,反之則根據原來的值傳入下一個方法去完成下一步的擴容判斷
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//{}
            return Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY=10,minCapacity=0+1
        }
        return minCapacity;
    }
    //minCapacity表示修改后的數組容量,add方法調用ensureCapacityInternal時傳入minCapacity = size + 1 
    private void ensureCapacityInternal(int minCapacity) {
        //判斷看看是否需要擴容
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

ensureExplicitCapacity方法

下面談談ensureExplicitCapacity方法(modCount涉及到Java的快速報錯機制后面會談到),可以看到如果修改后的數組容量大於當前的數組長度那么就需要調用grow進行擴容,反之則不需要。

        //判斷當前ArrayList是否需要進行擴容
    private void ensureExplicitCapacity(int minCapacity) {
        //快速報錯機制
        modCount++;
 
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

grow方法

最后看下ArrayList擴容的核心方法grow(),下面將針對三種情況對該方法進行解析:

  1. 當前數組是由默認構造方法生成的空數組並且第一次添加數據。此時minCapacity等於默認的容量(10)那么根據下面邏輯可以看到最后數組的容量會從0擴容成10。而后的數組擴容才是按照當前容量的1.5倍進行擴容;
  2. 當前數組是由自定義初始容量構造方法創建並且指定初始容量為0。此時minCapacity等於1那么根據下面邏輯可以看到最后數組的容量會從0變成1。這邊可以看到一個嚴重的問題,一旦我們執行了初始容量為0,那么根據下面的算法前四次擴容每次都 +1,在第5次添加數據進行擴容的時候才是按照當前容量的1.5倍進行擴容。
  3. 當擴容量(newCapacity)大於ArrayList數組定義的最大值后會調用hugeCapacity來進行判斷。如果minCapacity已經大於Integer的最大值(溢出為負數)那么拋出OutOfMemoryError(內存溢出)否則的話根據與MAX_ARRAY_SIZE的比較情況確定是返回Integer最大值還是MAX_ARRAY_SIZE。這邊也可以看到ArrayList允許的最大容量就是Integer的最大值(-2的31次方~2的31次方減1)。
    //ArrayList擴容的核心方法,此方法用來決定擴容量
    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);
    }
 
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
 
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


免責聲明!

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



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