ArrayList擴容機制的源碼詳解
一:ArrayList的構造函數:
ArrayList的構造函數源碼有三種:
先來看看ArrayList底層定義的一些變量的含義:
/** Default initial capacity
* 默認的容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/** Shared empty array instance used for empty instances.
* 定義的空的數組
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
* 不可以被序列化的數組
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
* 這個list集合的長度
* @serial
*/
private int size;
/**
* Constructs an empty list with an initial capacity of ten
* 空的構造函數
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
* 根據用戶傳入的容量大小構造一個list集合,長度可以大於等於0,但是如果為負數會拋出異常
*/
public ArrayList(int initialCapacity) {
// 如果初始容量大於0
if (initialCapacity > 0) {
// 創建一個大小為initialCapacity的數組
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 創建一個空數組
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 如果為負數,直接拋出異常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
* 構造包含指定collection元素的列表,這些元素利用該集合的迭代器按順序返回
* 如果指定的集合為null,throws NullPointerException。
*/
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的擴容機制
主要來分析一下無參的構造函數:先來看看add()方法
1:add()方法的實現
// 將指定的元素加到列表的末尾
public boolean add(E e) {
// 添加元素之前,先調用ensureCapacityInternal方法
ensureCapacityInternal(size + 1); // Increments modCount!!
// 這里看到的ArrayList添加元素的實質相當於為數組賦值
elementData[size++] = e;
return true;
}
2:ensureCapacityInternal方法的實現
// 得到最小的擴容量
private void ensureCapacityInternal(int minCapacity) {
// 當一開始是默認空的列表
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 獲取默認的容量和傳入參數的最大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
例如:當add進第一個元素時候,minCapacity為1,此時去二者的最大值
3:比較ensureExplicitCapacity
// 判斷是否需要擴容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// 調用grow方法進行擴容
grow(minCapacity);
}
我們來將上面的內容梳理一下,會get到幾個點:
*當我們add進第一個元素到ArrayList時, elementData.length 為0 (因為還是一個空的 list) ,但是此時執行了ensureCapacityInternal()方法,通過默認的比較,此時會得到minCapacity為10。此時minCapacity - elementData.length > 0滿足,所以會進入grow(minCapacity)方法.
*當add第二個元素時, minCapacity 為2,此時e lementData.length(容量)在添加第一個元素后擴容成 10 了。此時,minCapacity - elementData.length > 0 不成立,所以不會進入 (執行)grow(minCapacity) 方法。
*同理添加第3,4,5,6.....一直到11個元素都不會grow,當到第11個元素的時候,minCapacity(11)比10大,會進行grow操作
4:grow()方法(擴容核心)
// 需要分配的數組大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
// 集合的容量
int oldCapacity = elementData.length;
// 新的集合的容量(在這里運用了位運算,位運算是計算機最快的,右移一位,所以新容量是1.5倍)
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新容量小於添加的集合的容量,則把該容量替換
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
/** 如果新容量大於 MAX_ARRAY_SIZE,進入(執行) `hugeCapacity()` 方法來比較 minCapacity 和 * MAX_ARRAY_SIZE,如果minCapacity大於最大容量,則新容量則為`Integer.MAX_VALUE`,否則, * 新容量大小則為 MAX_ARRAY_SIZE 即為 `Integer.MAX_VALUE - 8`。
*/
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 將原數組copy到新的數組中
elementData = Arrays.copyOf(elementData, newCapacity);
}
/** 如果新容量大於 MAX_ARRAY_SIZE,進入(執行) `hugeCapacity()` 方法來比較 minCapacity 和 * MAX_ARRAY_SIZE,如果minCapacity大於最大容量,則新容量則為`Integer.MAX_VALUE`,否則, * 新容量大小則為 MAX_ARRAY_SIZE 即為 `Integer.MAX_VALUE - 8`。
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
補充:System.arrayCopy()和Arrays.copyOf()方法
聯系: 看兩者源代碼可以發現 copyOf() 內部實際調用了 System.arraycopy() 方法
區別: arraycopy() 需要目標數組,將原數組拷貝到你自己定義的數組里或者原數組,而且可以選擇拷貝的起點和長度以及放入新數組中的位置 copyOf() 是系統自動在內部新建一個數組,並返回該數組。
參考原文:
https://www.cnblogs.com/baichunyu/p/12965241.html