Java数组与泛型

Java数组与泛型

之前一直对数组和泛型存在一些疑惑,故总结如下。

一、基础类型与引用类型的数组

请看如下代码:
package org.lin;

public class Test1 {
    @SuppressWarnings("unused")
    public static void main(String[] args) {
        Object[] objs = new Object[5];
        System.out.println(objs.getClass());
        
        Double[] doubles = new Double[5];
        System.out.println(doubles.getClass());
        
        double[] basicDoubles = new double[5];
        System.out.println(basicDoubles.getClass());
        
        if (doubles instanceof Object[]) {
            System.out.println("Double[]是Object[]的子类");
        }
        
        /*  直接编译出错
        if (!(basicDoubles instanceof Object[])) {
            
        }
        */

        //子类强转为父类,抛java.lang.ClassCastException
        //Double[] copy = (Double[])objs;
        
        //自定义类型也是一样
        Super[] supers = new Sub[5];
    }
    
    static class Super {}
    static class Sub extends Super {}
}

从上述代码可以看出以下两点:
1.Java中,基础类型的数组区别于特殊类型的数组
2.引用类型的数组之间存在继承关系,其继承关系取决于数组元素之间的继承关系:
a. Object[]类型是所有引用数组类型的父类,如Double[]类型是Object[]类型的子类
b. 对于自定义类型也是一样,如上述代码中,Super[]类型是Sub[]类型的父类

对于引用类型的数组,可以通过如下方式创建:
package org.lin;

import java.lang.reflect.Array;

public class Test2 {
    @SuppressWarnings("unused")
    public static void main(String[] args) {
        Integer[] ints = (Integer[])Array.newInstance(Integer.class, 5);
        Integer[] ints2 = (Integer[])Array.newInstance(ints.getClass().getComponentType(), ints.length);
    }
}

二、泛型对数组的处理

Java是不支持泛型数组的,但是在泛型类和泛型数组中,定义泛型数组的引用是允许的。
package org.lin;

public class Test4 {
    
    public static void main(String[] args) {
        //泛型类的特殊用法
        MyList<Integer> list = new MyList<>();
        
        //Object[]强转为Integer[],抛java.lang.ClassCastException
        //Integer[] data = list.getData();
        
        //隐藏内部实现,对外提供接口是可以的
        list.add(1);
        System.out.println(list.get(0));
    }
    
    static class MyList<T> {
        //由于类型擦除,T[]类型最终变为Object[]类型
        @SuppressWarnings("unchecked")
        private T[] data = (T[])new Object[5];
        
        //另一种定义方式,更加安全,但需要手动强转
        //private Object[] data = new Object[5];
        
        private int size = 0;
        
        /**
         * 不要对外提供getter方法,因为会导致强转异常
         * @return
         */
        public T[] getData() {
            return data;
        }
        
        public void add(T element) {
            data[size++] = element;
        }
        
        public T get(int index) {
            //编译后,代码为   return (T)data[index];
            return data[index];
        }
    }
}



三、数组的复制

jdk提供了native方法对数组的复制提供了支持,并对它进行了一些封装:

package org.lin;

import java.util.ArrayList;
import java.util.Arrays;

public class Test3 {
    @SuppressWarnings("unused")
    public static void main(String[] args) {
        Double[] doubles = new Double[5];
        double[] basicDoubles = new double[5];
        
        //使用native方法复制数组
        double[] basicDoublesCopy = new double[basicDoubles.length];
        System.arraycopy(basicDoubles, 0, basicDoublesCopy, 0, basicDoubles.length);
        
        //Arrays对System.arraycopy的封装
        Object[] doublesToObjects = Arrays.copyOf(doubles, doubles.length, Object[].class);
        double[] basicDoublesCopy2 = Arrays.copyOf(basicDoubles, basicDoubles.length);
        
        //ArrayList转为数组
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        
        //无参的toArray,返回的是Object[]类型
        Object[] objs = list.toArray(); 
        
        //正确使用toArray的方式
        Integer[] ints = list.toArray(new Integer[list.size()]);
    }
}
Arrays.copyOf源码分析:
1.基础类型
对于每种基础类型,都提供了重载的copyOf方法:
    /**
     * Copies the specified array, truncating or padding with zeros (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>0d</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     *
     * @param original the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with zeros
     *     to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException if <tt>original</tt> is null
     * @since 1.6
     */
    public static double[] copyOf(double[] original, int newLength) {
        double[] copy = new double[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
2.引用类型
实现并不难理解,copyOf内部会使用Array.newInstance实例化引用数组类型,然后调用System.arraycopy执行复制操作

    /**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of exactly the same class as the original array.
     *
     * @param <T> the class of the objects in the array
     * @param original the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with nulls
     *     to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException if <tt>original</tt> is null
     * @since 1.6
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

    /**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of the class <tt>newType</tt>.
     *
     * @param <U> the class of the objects in the original array
     * @param <T> the class of the objects in the returned array
     * @param original the array to be copied
     * @param newLength the length of the copy to be returned
     * @param newType the class of the copy to be returned
     * @return a copy of the original array, truncated or padded with nulls
     *     to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException if <tt>original</tt> is null
     * @throws ArrayStoreException if an element copied from
     *     <tt>original</tt> is not of a runtime type that can be stored in
     *     an array of class <tt>newType</tt>
     * @since 1.6
     */
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
ArrayList.toArray源码分析:
1.无参的toArray
elementData属性是Object[]类型的,因此会返回Object[]类型的数组
    /**
     * Returns an array containing all of the elements in this list
     * in proper sequence (from first to last element).
     *
     * <p>The returned array will be "safe" in that no references to it are
     * maintained by this list.  (In other words, this method must allocate
     * a new array).  The caller is thus free to modify the returned array.
     *
     * <p>This method acts as bridge between array-based and collection-based
     * APIs.
     *
     * @return an array containing all of the elements in this list in
     *         proper sequence
     */
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
2.有参的toArray:
1.当a.length < size,创建一个新的数组并返回
2.当a.length == size,执行复制,并返回
3.当a.length > size,将最后一个元素之后的位置设为null
因此,最佳的做法是将一个同类型的,length == size的数组作为参数调用toArray方法。

    /**
     * Returns an array containing all of the elements in this list in proper
     * sequence (from first to last element); the runtime type of the returned
     * array is that of the specified array.  If the list fits in the
     * specified array, it is returned therein.  Otherwise, a new array is
     * allocated with the runtime type of the specified array and the size of
     * this list.
     *
     * <p>If the list fits in the specified array with room to spare
     * (i.e., the array has more elements than the list), the element in
     * the array immediately following the end of the collection is set to
     * <tt>null</tt>.  (This is useful in determining the length of the
     * list <i>only</i> if the caller knows that the list does not contain
     * any null elements.)
     *
     * @param a the array into which the elements of the list are to
     *          be stored, if it is big enough; otherwise, a new array of the
     *          same runtime type is allocated for this purpose.
     * @return an array containing the elements of the list
     * @throws ArrayStoreException if the runtime type of the specified array
     *         is not a supertype of the runtime type of every element in
     *         this list
     * @throws NullPointerException if the specified array is null
     */
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }





猜你喜欢

转载自blog.csdn.net/qq_21508059/article/details/79052478