面試題目
面試官:“ArrayList集合默認長度是多少?”。
完美的回答:默認長度爲10。但是ArrayList的默認長度是有jdk版本差異的,在jdk8版本之前默認長度是10。而在jdk8版本的時候對ArrayList數組的默認長度進行了優化,將原來的默認長度10,改爲了初始長度爲0。當我們在首次添加元素,需要分配數組空間時,jdk自動幫我們進行了擴容操作,將初始數組長度擴容成了10。這樣做有效地降低了無用內存的佔用!
另外,它利用了數組擴容的特性來完成集合的這些功能,這也就是ArrayList集合查詢快、增刪慢的原因了!(如果你對這些集合的特點了解,可以附加這些內容,感覺爲了保險還是不要說,以免自己給自己挖坑!)
知識摘要
面臨這個問題,我們一定不要直接回答默認長度爲10或者0。
因爲ArrayList集合源於JDK的1.2版本,他開始的時候默認長度在jdk源碼裏是初始了10個長度。
而經過歷年的迭代,人們發現了問題,並在JDK8版本的時候做了優化。初始長度爲0,而在首次添加元素,需要實際分配數組空間,執行數組擴容操作時,擴容長度爲10。
優點:真正向數組中插入數據,用的時候再創建,或再加載,有效的降低無用內存的佔用
源碼(追完源碼你即可瞭解jdk是如何幫你實現的)
用於默認大小的空實例的共享空數組實例。我們將其與空的ELEMENTDATA區別開來,以便在添加第一個元素時需要的擴容
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
//一個空對象,如果使用默認構造函數創建,則默認對象內容默認是該值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
這是當有元素插入到數組中要擴容的默認長度10
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
Object數組
/**
* 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
空對象
// 一個空對象
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
當前數組長度,也可以理解爲有效元素個數
// 當前數組長度
private int size;
數組最大長度
// 數組最大長度
private static final int MAX_ARRAY_SIZE = 2147483639;
add方法
add的方法有兩個,一個是帶一個參數的,一個是帶兩個參數的,下面我們將一個參數的add方法
add(E e) 方法
add主要的執行邏輯如下:
1)確保數組已使用長度(size)加1之後足夠存下 下一個數據
2)修改次數modCount 標識自增1,如果當前數組已使用長度(size)加1後的大於當前的數組長度,則調用grow方法,增長數組,grow方法會將當前數組的長度變爲原來容量的1.5倍。
3)確保新增的數據有地方存儲之後,則將新元素添加到位於size的位置上。
4)返回添加成功布爾值。
插入元素入口
//注:size我在這裏理解爲有效元素個數
public boolean add(E e) {
ensureCapacityInternal(size + 1); //判斷擴容
elementData[size++] = e;//將插入的值放在數組中(有效元素個數+1)
return true;//返回添加成功的布爾值true
}
判斷擴容入口
private void ensureCapacityInternal(int minCapacity) {
//查看ensureExplicitCapacity和calculateCapacity方法源碼
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
ensureExplicitCapacity
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//忽略此句,這是父接口的計數器
// overflow-conscious code
if (minCapacity - elementData.length > 0)//傳入的參數(size+1)-Object數組長度>0的時候需要擴容
grow(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;//原長度賦給oldCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);//將數組長度擴容爲原來的1.5倍
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);//複製數組元素到新長度的數組中
}
calculateCapacity
//確保添加的元素有地方存儲,當第一次添加元素的時候this.size+1 的值是1,所以第一次添加的時候會將當前elementData數組的長度變爲10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//Object數組長度==默認長度0時
return Math.max(DEFAULT_CAPACITY, minCapacity);//返回默認長度10和傳入(size-1)的最大追
}
return minCapacity;//如果Object數組長度不等於默認長度0時,直接返回(size-1)
}