一文講清ArrayList擴容機制


對於ArrayList大家一定不會陌生,它就是傳說中的動態數組。ArrayList 的底層是數組隊列,相當於動態數組。與 Java 中的數組相比,它的容量能動態增長,使用起來十分便利。

它繼承於 AbstractList,實現了List、RandomAccess、Cloneable這些接口。 對於List和Cloneable大家一定都很熟悉,RandomAccess就不一定了。其實RandomAccess 是一個標志接口,表明實現這個這個接口的 List 集合是支持快速隨機訪問的。在ArrayList中我們可以通過get()快速訪問某個元素。

介紹完了ArrayList接下來我們進入正題,談一談ArrayList的擴容機制。、

ArrayList的構造函數

 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_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 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;
        }
    }

 

我們可以看到,上面是ArrayList的三個構造函數,如果說我們不會傳遞參數進去,那么就會默認的去調用無參的構造函數,那么ArrayList的容量就是默認大小也就是10。
如果我們傳遞一個參數進入ArrayList,那么ArrayList的默認大小就是這個參數的值。

ArrayList的add()方法

既然我們是討論擴容機制,那么什么情況下才會引發擴容?沒錯,就是你向ArrayList中添加一個元素的時候,它才會觸發擴容機制。我們看一下add()的源碼。

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

我們可以看到,在把e賦值給數組之前,add方法調用了ensureCapacityInternal()方法,來判斷是否需要擴容。那么接下來我們就沿着ensureCapacityInternal()方法的源代碼,一步步去了解擴容的原理。

 

ArrayList的擴容機制

 

ensureCapacityInternal()方法

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

        ensureExplicitCapacity(minCapacity);
    }

ensureCapacityInternal方法接受了size+1作為minCapacity,並且進行判斷如果數組是空數組,那么10和minCapacity的較大值就作為新的minCapacity。接下來會傳遞minCapacity到ensureExplicitCapacity()方法中做進一步的判斷。

 

ensureExplicitCapacity()方法

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

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

接下來會判斷我們傳入的minCapacity 和 elementData.length的大小,如果 elementData.length大於minCapacity,說明數組容量夠用,就不需要擴容。反之,則傳入minCapacity到grow()方法中,開始擴容。

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);
    }

 

進入grow方法,我們會將newCapacity設置為舊容量的1.5倍,這也是ArrayList每次擴容都為原先容量1.5倍的由來。然后會進行判斷,如果newCapacity小於minCapacity,那么就將minCapacity的值賦予newCapacity

然后再檢查新容量是否超出了定義的最大容量,如果超出則調用hugeCapacity方法,比較minCapacity和MAX_ARRAY_SIZE的值。如果minCapacity大,那么新容量為Integer.MAX_VALUE,否則新容量為MAX_ARRAYSIZ。最后調用Arrays.CopyOf傳遞elementData和新容量,返回新的elementData。

 

希望你讀完本文有所收獲!!

 


免責聲明!

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



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