在月读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中的toArray()方法)
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[ ]