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中的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[ ]

原文地址:https://www.cnblogs.com/xuchao0506/p/14971652.html