ArrayList
有多個不一樣的構造函數,不一樣的構造函數的初始容量是不一樣的。快速看一下ArrayList
源碼裏關於元素存放的幾個私有屬性:java
// 默認容量是10
private static final int DEFAULT_CAPACITY = 10;
// 若是容量爲0的時候,就返回這個數組
private static final Object[] EMPTY_ELEMENTDATA = {};
// 使用默認容量10時,返回這個數組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 元素存放的數組
transient Object[] elementData;
// 元素的個數
private int size;
// 記錄被修改的次數
protected transient int modCount = 0;
// 數組的最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
複製代碼
ArrayList
有三個構造方法,不一樣的構造方法的容量是不同的,具體能夠查看JDK 源碼。數組
elementData
爲DEFAULTCAPACITY_EMPTY_ELEMENTDATA
elementData
爲EMPTY_ELEMENTDATA
。toArray()
方法把它變成一個數組並賦值給elementData
。一樣會判斷它的長度是否爲0,若是爲0,設置elementData
爲EMPTY_ELEMENTDATA
。能夠看到,ArrayList
裏面有兩個概念,一個是capacity
,它表示的就是「容量」,其實質是數組elementData
的長度。而size
則表示的「存放的元素的個數」。函數
由於Java中,數組操做不能越界,因此咱們必需要保證在插入操做的時候,不會拋出數組越界異常。性能
底層主要是這三個私有方法:spa
// 擴容一個
private Object[] grow() {
return grow(size + 1);
}
// 保證擴容到指望容量minCapacity及以上
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
// 根據指望容量minCapacity計算實際須要擴容的容量
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; // 獲得舊容量
int newCapacity = oldCapacity + (oldCapacity >> 1); // 設置新容量爲舊容量的1.5倍
if (newCapacity - minCapacity <= 0) { // 若是新容量仍然小於指望容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // 若是是使用的默認容量
return Math.max(DEFAULT_CAPACITY, minCapacity); // 取默認容量和指望容量較大值返回
if (minCapacity < 0) // overflow // 檢查指望容量是否越界(int 的範圍)
throw new OutOfMemoryError();
return minCapacity; // 返回指望容量
}
// 若是新容量大於指望容量,判斷一下新容量是否越界
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
複製代碼
能夠看到,底層實際上是調用了Arrays.copyOf
方法來進行擴充數組容量的。這裏咱們主要看一下最後一個方法newCapacity(int minCapacity)的實現。code
默認狀況下,新的容量會是原容量的1.5倍,這裏用了位運算提升效率。通常狀況下,若是擴容1.5倍後就大於指望容量,那就返回這個1.5倍舊容量的值。而若是小於指望容量,那就返回指望容量。這裏對默認容量10作了特殊處理。內存
使用1.5倍這個數值而不是直接使用指望容量,是爲了防止頻繁擴容影響性能。試想若是每次add操做都要擴容一次,那性能將會很是低下。ci
上述grow方法其實主要是用於實現自動擴容的。而用戶也能夠經過調用如下方法實現手動擴容:element
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
複製代碼
爲何須要手動擴容?試想一下,若是用戶已經知道即將存入大量的元素,好比目前有20個元素,即將存入2000個。那這個時候使用自動擴容就會擴容屢次。而手動擴容能夠一次性擴容到2000,能夠提升性能。rem
ArrayList
沒有縮容。不管是remove
方法仍是clear
方法,它們都不會改變現有數組elementData
的長度。可是它們都會把相應位置的元素設置爲null
,以便垃圾收集器回收掉不使用的元素,節省內存。