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;
}