對於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。
希望你讀完本文有所收獲!!