動態擴容
1、add(E e)方法中
① ensureCapacityInternal(size+1),確保內部容量,size是添加前數組內元素的數量
② elementData[size++] = e 添加元素到相應位置,元素數量加1
2、 ensureCapacityInternal(size+1)確保內部容量
① 計算最小需要空間(如果傳入的是個空數組則最小容量取默認容量與minCapacity之間的最大值)
② 判斷是否需要擴容(如果最小需要空間比elementData的內存空間要大,則擴容)
3、擴容 grow(int minCapacity)
newCapacity=oldCapacity + (oldCapacity >> 1),原來容量的1.5倍
再與最小需要空間比較,與最大數組長度比較
一 初始化
先看一下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); } }
在無參構造中,我們看到了在用無參構造來創建對象的時候其實就是創建了一個空數組,長度為0
在有參構造中,傳入的參數是正整數就按照傳入的參數來確定創建數組的大小,否則異常
二 確保內部容量
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
① ensureCapacityInternal方法名的英文大致是“確保內部容量”,size表示的是執行添加之前的元素個數,
並非ArrayList的容量,容量應該是數組elementData的長度。ensureCapacityInternal該方法通過將現有的元素個數與數組的容量比較。看如果需要擴容,則擴容。
②是將要添加的元素放置到相應的數組中。
可以看出ensureCapacityInternal()是用來擴容的,形參為最小擴容量,進入此方法后:
private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
通過方法calculateCapacity(elementData, minCapacity)獲取最小需要空間
private static int calculateCapacity(Object[] elementData, int minCapacity) { //如果傳入的是個空數組則最小容量取默認容量與minCapacity之間的最大值 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
ensureExplicitCapacity方法可以判斷是否需要擴容:
private void ensureExplicitCapacity(int minCapacity) { modCount++; // 如果最小需要空間比elementData的內存空間要大,則需要擴容 if (minCapacity - elementData.length > 0) //擴容 grow(minCapacity); }
三 擴容
ArrayList擴容的關鍵方法grow():
private void grow(int minCapacity) { // 獲取到ArrayList中elementData數組的內存空間長度 int oldCapacity = elementData.length; // 擴容至原來的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); // 再判斷一下新數組的容量夠不夠,夠了就直接使用這個長度創建新數組, // 不夠就將數組長度設置為需要的長度 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //若預設值大於默認的最大值檢查是否溢出 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 調用Arrays.copyOf方法將elementData數組指向新的內存空間newCapacity的連續空間 // 並將elementData的數據復制到新的內存空間 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; }
int newCapacity = oldCapacity + (oldCapacity >> 1);
oldCapacity >> 1 右移運算符 原來長度的一半 再加上原長度也就是每次擴容是原來的1.5倍
之前的所有都是確定新數組的長度,確定之后就是把老數組copy到新數組中,這樣數組的擴容就結束了
###每次擴容都是通過Arrays.copyOf(elementData, newCapacity) 這樣的方式實現的。ArrayList的自動擴容機制底層借助於System實現System.arraycopy(0,oldsrc,0,newsrc,length);
擴展:System源碼中的arraycopy()標識為native意味JDK的本地庫,不可避免的會進行IO操作,如果頻繁的對ArrayList進行擴容,毫不疑問會降低ArrayList的使用性能,因此當我們確定添加元素的個數的時候,我們可以事先知道並指定ArrayList的可存儲元素的個數,這樣當我們向ArrayList中加入元素的時候,就可以避免ArrayList的自動擴容,從而提高ArrayList的性能。