ArrayList有參構造方法 中 elementData.getClass() != Object[].class 的探究
在月讀ArrayList原始碼的時候,在ArrayList的構造方法ArrayList(Collection<? extends E> c) 中看到了一行程式碼,感覺有些困惑,elementData.getClass() != Object[].class 為啥會不等呢?
/** * 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; } }
於是就輸出相關程式碼來進行測試驗證:
public static void main(String[] args) { Object[] oo = {}; List<String> list = new ArrayList<>(); list.add("aa"); System.out.println(list.getClass()); System.out.println(list.toArray().getClass()); System.out.println(list.toArray().getClass()== oo.getClass()); System.out.println(); list = Arrays.asList("abc"); System.out.println(list.getClass()); System.out.println(list.toArray().getClass()); System.out.println(list.toArray().getClass()== oo.getClass()); System.out.println(); System.out.println(oo.getClass()); }
執行結果:
可以看到兩次輸出list的型別都不一樣,一個是java.util.ArrayList,一個是java.util.Arrays$ArrayList;
list呼叫toArray()方法後得到的Class型別也不一樣,一個是java.lang.Object,一個是java.lang.String,
所以用他們和Object[]的型別進行比較的時候,也不一樣,一個true,一個false。
解釋:為什麼兩次list不一樣?
第一個是直接使用new ArrayList()例項化的物件,所以他的型別是java.uitl.ArrayList。第二個是使用的是Arrays.asList()方法初始化的物件,看方法asList()的原始碼,如下所示:
/**
* Returns a fixed-size list backed by the specified array. (Changes to
* the returned list "write through" to the array.) This method acts
* as bridge between array-based and collection-based APIs, in
* combination with {@link Collection#toArray}. The returned list is
* serializable and implements {@link RandomAccess}.
*
* <p>This method also provides a convenient way to create a fixed-size
* list initialized to contain several elements:
* <pre>
* List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
* </pre>
*
* @param <T> the class of the objects in the array
* @param a the array by which the list will be backed
* @return a list view of the specified array
*/
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
該方法返回的是一個ArrayList,但是此ArrayList非彼ArrayList,這個返回的ArrayList是Arrays自己的一個靜態內部類,如下所示:
/** * @serial include */ private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } //··· }
可以看到這個靜態內部類實現了RandomAccess、Serializable介面,即支援快速隨機訪問,支援序列化,這就支援了,向java.util.ArrayList的轉化。所以asList(T... a)方法的註釋上,有一句很妙的解釋:
This method acts as bridge between array-based and collection-based APIs, in combination with {@link Collection#toArray}.
此方法與 {@link Collection#toArray} 相結合,充當基於陣列和基於集合的 API 之間的橋樑。
所以第二個list輸出型別是java.util.Arrays$ArrayList 也就是Arrays自己的ArrayList,並且這個list底層也是陣列。
解釋:為什麼list呼叫toArray()方法後得到的Class型別也不一樣,一個是java.lang.Object,一個是java.lang.String ??
先看一個例子:
public static void main(String[] args) { String[] s = {"a"}; System.out.println(s.getClass()); System.out.println(); String[][] ss = {{"a"}}; System.out.println(ss.getClass()); System.out.println(); String[] a = {"a"}; Integer[] b = {1}; Object[] c = {new Object()}; System.out.println(a.getClass()); System.out.println(b.getClass()); System.out.println(c.getClass()); }
輸出如圖:
紅方框中表示的是陣列的維度,s是一維陣列, ss是二維陣列,最後看後三行,發現a、b、c輸出的是String、Integer、Object,這說明了一個問題,對陣列進行getClass()方法呼叫,得到的是陣列用來存放元素的型別。
回到問題,為什麼list呼叫toArray()方法後得到的Class型別也不一樣,一個是java.lang.Object,一個是java.lang.String ??
第一個list,也就是直接用ArrayList例項化的list,他的toArray()方法的原始碼,如下:
public Object[] toArray() { return Arrays.copyOf(elementData, size); }
返回的是一個明確的Object型別的陣列物件,因此呼叫第一個list的toArray()後在呼叫getClass()方法 輸出的就是java.lang.Object
第二個list,被Arrays.asList()例項化後的list,他的toArray()方法原始碼,如下:(注意這裡應該是Arrays自己的ArrayList中的toList方法)
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } @Override public int size() { return a.length; } @Override public Object[] toArray() { return a.clone(); } //... }
可以看到Arrays的ArrayList的toArray()方法返回的是 a.clone(),而a的宣告型別為 E [ ],也就是泛型化的,即ArrayList被例項化為什麼型別的,a.clone()就是什麼型別的。
程式碼中Arrays.asList("abc"),是String[ ]型別,所以這裡呼叫toArray()後,返回a.clone()方法得到的是一個String [ ], 對String[ ]在呼叫getClass()方法,得到的就是java.lang.String
所以在java.util.ArrayList的有參構造方法中,會有一行型別判斷
/**
* 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;
}
}
所以這裡要加上特殊情況,如果陣列緩衝區elementData的元素個數大於0,並且型別不是Object[ ]的,就要通過Arrays.copyOf()方法,來轉化為Object[ ]