環境:JDK1.8
先看看幾個ArrayList内部成員變量:
ArrayList的容量大小
預設的初始容量為10:DEFAULT_CAPACITY = 10。
對于所有的空執行個體可共享的空陣列執行個體EMPTY_ELEMENTDATA。
對于所有的空執行個體可共享的空陣列執行個體DEFAULTCAPACITY_EMPTY_ELEMENTDATA,不同于EMPTY_ELEMENTDATA在于該數組可記錄第一個元素被添加的時候,容器至少需要多大的空間
存儲ArrayList元素的數組緩沖區,ArrayList的容量即為該陣列資料緩沖區的長度。當添加第一個元素時,任意剛開始容量為DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList的容量都會擴充到DEFAULT_CAPACITY,也就是10。
内部數組可以配置設定的最大容量MAX_ARRAY_SIZE=Integer.MAX_VALUE(為整型數最大值)-8,為何要減8呢?因為這個8的空間用于存儲數組_length字段。
檢視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;
}
檢視ensureCapacityInternal(int minCapacity)方法、ensureExplicitCapacity(int minCapacity)方法以及calculateCapacity(Object[] elementData, int minCapacity)方法:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
ensureExplicitCapacity方法實作:
1. modCount++。
2. 如果需擴充的容量大小minCapacity大于原内部數組大小,則調用grow方法。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
calculateCapacity方法實作:
1. 傳入原内部數組elementData和需擴充的容量大小minCapacity。
2. 如果elementData為空數組則比較預設初始容量DEFAULT_CAPACITY=10與參數minCapacity的大小,取大傳回。
3. 如果elementData不為空,則傳回minCapacity。
檢視grow(int minCapacity)方法:
增加容量以確定ArrayList能保留指定的最小容量參數下的元素數量
/**
* 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);
}
grow方法實作:
a. 取得elementData的長度為原容量oldCapacity。
b. oldCapacity >> 1表示位運算,為oldCapacity轉換成二進制後向右移動1位,目前可了解為java中oldCapacity / 2的結果,以後會專門寫一篇文章來說明位運算。故可以了解為新容量newCapacity為oldCapacity+1/2的oldCapacity,此時再比較newCapacity和參數要求容量(至少得需要到參數要求的容量大小,不然無法将元素存放到ArrayList),如果小于則newCapacity設定為minCapacity,否則再比較newCapacity和MAX_ARRAY_SIZE,如果前者大于後者,則檢視hugeCapacity(int minCapacity)方法
當容量參數小于0,抛出異常OutOfMemoryError()。如果參數大于MAX_ARRAY_SIZE,則傳回Integer.MAX_VALUE(為整型數最大值),否則傳回MAX_ARRAY_SIZE。最後将傳回值賦給newCapacity
c. 将原内部數組和新的容量長度融合為新的内部數組對象(内容不變,容量擴充)。
結論:
1. ArrayList對象群初始化時可不傳參,共享内部數組對象,大小為空。
2. 當添加第一個元素時,内部擴容為預設大小10。
3. 當添加到大于10個元素時,則以後每次需擴容時,規則為newCapacity(新容量) = oldCapacity(原容量) +oldCapacity/2。
4. 将最新添加的元素放入目前ArrayList内部數組的size++位,即elementData[size++] = e;
額外說明:ArrayList從字面上看,底層是用數組實作的,增删查等操作也和數組的這類操作特性一緻。
打個比方:
1. 添加第1個元素則内部數組擴充為預設大小10即elementData的大小為10。
2. 添加第11個元素時,則一直執行到grow方法,傳參為11,oldCapacity為10,newCapacity變為15,elementData的大小為15,size當下為15,elementData[size++] = e,設定第11個數組元素。
3. 添加第16個元素時,則一直執行到grow方法,傳參為16,oldCapacity為15,newCapacity變為22,elementData的大小為22,size當下為15,elementData[size++] = e,設定第16個數組元素。
以此類推...
解決具體面試題:
public static ArrayList<Integer> getIntegerList(int capacity) {
ArrayList<Integer> list = new ArrayList<Integer>();
if (capacity <= 0) {
return list;
}
for (int i = 0; i < capacity; i++) {
list.add(i);
}
return list;
}
public static void main(String[] args) {
getIntegerList(64);
}
問題:以上程式中的list擴容了幾次?
結合上述内容,則可以繼續推倒:
4. 當添加第23個元素時,則newCapacity變為33,elementData的大小為33,設定第23個數組元素。
5. 當添加第34個元素時,則newCapacity變為51,elementData的大小為51,設定第34個數組元素。
6. 當添加第52個元素時,則newCapacity變為78,elementData的大小為78,設定第52個數組元素。
64在第78個元素之前,則經過6次擴容即可滿足list.add(63)的需求。
答案為:6次