一、下酒菜
聊源碼之前,先看點關於ArrayList初始化的一些面試題
- ArrayList的數據結構是怎樣的?
- 爲什麼ArrayList中數組變量elementData,不使用private修飾?
- new ArrayList()和new ArrayList(0)有什麼區別?
- new ArrayList()時,它的數組長度是多少?
- new ArrayList(9)時,它的數組長度是多少?
- new ArrayList()時,add一個元素後,它的數組長度是多少?
- new ArrayList(0)時,add一個元素後,它的數組長度是多少?
- ArrayList中EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA兩個變量都表示空數組,有何作用?
二、ArrayList中跟初始化相關的屬性
1. 空數組:EMPTY_ELEMENTDATA
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
2. 空數組:DEFAULTCAPACITY_EMPTY_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 = {};
- 區分DEFAULTCAPACITY_EMPTY_ELEMENTDATA和DEFAULT_CAPACITY兩個空數組,是爲了知道在第一次執行add方法時,應該擴容多少。後面會提到
3. 默認初始大小
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
注意,這個默認初始大小是指new ArrayList()時,第一次add元素時的擴容大小,而不是new ArrayList()時數組的大小!
4. 存儲數據的數組
/**
* 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
- 註釋中提到,當elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA時(即new ArrayList()時),第一次add元素時擴容爲DEFAULT_CAPACITY(10)。對應上面3中的介紹。
- 不使用private是因爲方便嵌套類的訪問。這裏多提一下,語法層面上無論外部類屬性是不是private修飾的,內部類都可以訪問;但是如果外部類屬性被private修飾,內部類訪問該屬性時,編譯器會生成靜態的訪問方法來提供給內部類訪問。
- 這裏解釋下EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的區別,也就是上面2中的後續。當elementData爲EMPTY_ELEMENTDATA時第一次add後擴容爲1;當elementData爲DEFAULTCAPACITY_EMPTY_ELEMENTDATA時第一次add後擴容爲10。這樣有什麼好處呢?想不到,就擴大來想。如果十萬個ArrayList每個長度爲2就好了,爲什麼都初始化爲10呢?
三、ArrayList共有三個構造方法
1. 無參構造方法
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 可以看到該無參構造方法,是給elementData賦值爲一個空數組;所以new ArrayList()時,它的數組長度是0
- 此時空數組的值爲:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
2. 入參僅有一個整型的構造方法
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);
}
}
- 如果參數爲0時,空數組的值爲:EMPTY_ELEMENTDATA
- 如果參數大於0時,直接創建一個參數大小的數組。所以new ArrayList(9)時,它的數組長度就是9,不會改成10
3. 入參僅有一個Collection類型的構造方法
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;
}
}
上面代碼看起來有些奇怪,Collection.toArray()不就是返回Object[]的嗎?爲什麼還要判斷一下呢? 原因註釋上也寫了,c.toArray可能不能正確放回Object[]。寫個返回其他類型的代碼就明白了
private Object[] returnTest() {
return new String[10];
}
雖然方法聲明是Object[],但返回的引用可能是其他類型喲。再舉個實際情況的例子
@Test
public void constructCodeToArrayTest(){
List<String> asList = Arrays.asList("asList", "aa");
Object[] asListArray = asList.toArray();
System.out.println(asListArray.getClass());// class [Ljava.lang.String;
}
四、小結
把ArrayList的初始化源碼看完之後,在回頭看看上面的面試題是不是感覺很小兒科了?