1、先看一下ArrayList的構造方法
1-1:空參構造方法:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
可以看到默認的空參構造方法是賦值了一個空數組
1-2:傳入默認大小/容量,構造方法
private static final Object[] 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,就會給你new出相對應大小的數組。不然就會拋出非法參數的異常
- 因爲擴容是需要浪費時間的性能的。所以如果你開始就知道容量大小,應當直接選擇這種構造方法
1-2:傳入一個Collection的集合,構造方法
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集合
2、看一下ArrayList是如何進入擴容的
看一下add()方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
可以看到首先就是調用了這個 ensureCapacityInternal,因爲你添加一個元素,可能會出現數組越界,那這個方法就是預防這個問題的。
我們再看一下這個 ensureCapacityInternal
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
這個裏面先調用了calculateCapacity,後調用 ensureExplicitCapacity,我們依次來看
private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
- 這個方法就是判斷,目前這個集合/數組是否爲空,因爲我們看到使用空參構造出來的集合/數據是空的。如果是空的話。就取默認大小和當前大小的最大值。
- 可以看到默認容量大小是 10
上面看了calculateCapacity,我們再來看下 ensureExplicitCapacity
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0){
grow(minCapacity);
}
}
這個 minCapacity 就是我們現在需要的容量大小,如果它大於當前的數組大小。那麼就觸發了 grow() 方法,也就是擴容方法。
3、現在我們可以來具體看一下這個 grow() 擴容方法了
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
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);
}
簡單解釋一下 >> 右位移,你也可以直接記住是擴大了50%
這裏直接把oldCapacity當作 999
1、把 999 轉化成二進制 1111100111
2、從右邊去掉一位變成 111110011 就是最後的結果,轉成十進制是 499
3、你可以理解成 >> 後面接幾,就移除幾位
- 通過上面的解釋我們知道了新的容量大小是之前的1.5倍。
- 如果當前所需容量大小,還是大於新的容量大小。 就直接讓當前容量大小作爲擴容後的大小
- 如果新的容量要是大於默認最大值,就需要調用一下hugeCapacity方法,取Integer和當前容量的最大值
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
4、總結
- 當我們向集合裏面添加一個元素的時候,就會去判斷當前集合大小是否滿足。
- 因爲使用無參構造方法創建的ArrayList,默認是空數組。所以需要先判斷數組是否爲空,如果爲空的話,就取當前長度和默認長度的最大值,默認長度是10。
- 然後判斷當前所需空間是否大於數組的空間,如果不大於就不需要進行擴容了。如果大於則需要進去擴容。
- 新的長度大小,是在舊的基礎上加上舊長度的0.5倍。使用向右位移一位計算出來的。
- 如果新計算的長度還是小於當前需要的長度,就直接讓新的長度等於當前所需長度。
- 最後得出的新長度要和最大的數組長度對比。如果大於最大長度。就需要讓新長度和最大長度比較,大於最大長度,就讓新長度等於Integer的最大值,否則就讓新長度等於數組最大長度。
- 最後一步,得出了新的長度,調用Arrays.copyOf,生成新的數組。
感興趣的可以關注我的個人訂閱號: