ArrayList的擴容機制:
當向ArrayList中添加元素的時候,ArrayList的存儲容量如果滿足新元素的容量要求,則直接存儲;ArrayList的存儲容量如果不滿足新元素的容量要求,ArrayList會增強自身的存儲能力,以達到存儲新元素的要求。
因為不同的JDK版本的擴容機制可能有差異,下面以JDK1.8為例說明
一、構造方法
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; } }
二、主要方法
1. 參數說明
//默認的初始容量 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)方法
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方法
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()方法
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方法
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中添加元素時,拋出異常
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
https://blog.csdn.net/u010176014/article/details/52073339
