ArrayList 初始容量
ArrayList
有多個不同的構造方法,不同的構造方法的初始容量是不同的。介紹之前先看下 ArrayList
都有哪些變數
// 預設初始化容量=10
private static final int DEFAULT_CAPACITY = 10;
// 空陣列,當初始化容量為0時返回該陣列
private static final Object[] EMPTY_ELEMENTDATA = {};
// 空陣列,用於跟 EMPTY_ELEMENTDATA 區分開來,當使用預設構造方法建立的時候返回該陣列
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 元素存放的陣列
transient Object[] elementData;
// 元素個數(即list長度)
private int size;
// 記錄陣列被修改的次數
protected transient int modCount = 0;
// 陣列最大長度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
複製程式碼
無參構造方法,使用預設容量(預設容量為 10),並且設定
elementData
=DEFAULTCAPACITY_EMPTY_ELEMENTDATA
傳入初始容量(
initialCapacity
),判斷傳入容量的值。值 > 0,則 new 一個長度為initialCapacity
的 Object 陣列;值 < 0,直接設定elementData
=EMPTY_ELEMENTDATA
傳入一個
Collection
,如果Collection
的長度為 0,同樣的,設定elementData
=EMPTY_ELEMENTDATA
;否則,呼叫Collection
的toArray
方法建立一個陣列,並複製給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
*/
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.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 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
*/
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 相關定義
ArrayList
有兩個概念,一個是 capacity
,它表示“容量”
,表示的是 elementData
的長度。另一個是 size
,表示的是陣列中存放元素的個數
,這個很好理解,我們經常呼叫 ArrayList
的 size()
方法返回的就是這個 size
ArrayList 如何實現擴容的?
以無參構造方法為例,初始容量為 10,當呼叫 add(E e)
方法往 ArrayList
中插入資料的時候,才真正分配容量,即呼叫 Add(E e)
方法新增第一個元素時,陣列擴容為 10
以 add(E e)
方法為入口,分析原始碼
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
複製程式碼
每次呼叫 add(E e)
方法,都會呼叫 ensureCapacityInternal
方法,呼叫完 ensureCapacityInternal
方法後,將元素新增到 elementData
陣列的尾部,進入到 ensureCapacityInternal
方法
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
複製程式碼
add 第一個元素的時候,minCapacity
為 1 ,因為是新增第一個元素,此時 elementData
== DEFAULTCAPACITY_EMPTY_ELEMENTDATA
結果為true
,通過 Math.max
方法計算出 minCapacity
為 10
接下來看 ensureExplicitCapacity
方法,方法判斷 minCapacity
是否大於陣列長度,大於的話,表示陣列需要擴容,不擴容的話直接往陣列新增元素會導致陣列越界異常,掉呼叫 grow()
方法進行陣列的擴容
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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);
}
複製程式碼
預設情況下,新建容量(newCapacity
)會是原來容量(oldCapacity
)的 1.5 倍(這裡使用了 >> 位運算子提高效率),一般情況下,如果擴容 1.5 倍後就大於最小容量(minCapacity
),則使用這個 1.5 倍的容量,如果小於最小容量,就返回最小容量的值
當新建容量(newCapacity
)大於最大陣列長度(MAX_ARRAY_SIZE
)的時候,呼叫 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;
}
複製程式碼
該方法很簡單,當 minCapacity
< 0 的時候,丟擲記憶體溢位異常,否則返回最大陣列長度
最後,呼叫 Arrays.copyOf
方法新建一個原陣列的拷貝,並修改原陣列指向新建立的陣列。原陣列會被垃圾回收器回收
擴容總結
一般情況下,ArrayList
在呼叫 add
方法的時候,如果 size
長度不夠,則會進行擴容操作,將陣列長度擴容到原來的 1.5 倍。如果頻繁對 ArrayList
新增元素,則會頻繁執行擴容操作影響效能
為避免頻繁擴容造成效能下降。當知道將要存入陣列的元素數量的時候,可以呼叫 ArrayList(int initialCapacity)
構造方法顯示指定 ArrayList
的初始容量;或者可以通過手動呼叫 ensureExplicitCapacity(int minCapacity)
方法指定擴容長度,降低預設擴容頻率
ArrayList 有縮容嗎?
ArrayList
沒有縮容。無論是 remove
方法還是 clear
方法,它們都不會改變現有陣列 elementData
的長度。但是它們都會把相應位置的元素設定為 null
,以便垃圾收集器回收掉不使用的元素,節省記憶體