ArrayList是Java在中使用频率非常高的一个方法。我们在使用ArrayList的时候,经常需要将ArrayList中的对象转换为一个数组。
java已经对ArrayList进行了很好的封装,只需要调用ArrayList的toArray方法就可以从ArrayList对象中提取存放的对象的数组。
ArrayList重载了如下两个toArray方法:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
初学者在这两个方法的使用上获取会遇到一些困惑。下面结合笔者自己学习过程中遇到的困惑为大家详细介绍这两个toArray方法。
笔者在初学的时候在网络上看到过如下的简单代码
ArrayList list1 = new ArrayList();
System.out.println("size: " + list1.size());
for(int i=0; i<20; i++){
list1.add("hello");
}
String[] str = (String[])list1.toArray();
上面的代码先构造了一个ArrayList对象,然后往里面填了20个String对象,最后希望将ArrayList中的对象转换成一个数组。
执行上面的代码会报如下异常
[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
toArray()方法返回的数组为Object数组。上面的代码将Object数组强制转换为了String数组,属于将父类的引用强制转化为子类的引用。
java中,这种父类引用强制转化为子类引用只有在父类的引用指向的真实对象类型为所要强制转化的子类类型的时候,才不会报错。
下面举个小例子说明一下。
class Father{
String name;
int age;
}
class Son extends Father{
String girlFriend;
}
class Daughter extends Father{
String boyFriend;
}
public class test{
public static void main(String[] args)
{
Father f = new Son();
Son s = (Son)f;
Daughter d = (Daughter)f;
Father f2 = new Father();
Son s2 = (Son)f2;
}
}
String[] str = (String[])list1.toArray();报错很可能是由于toArray()方法返回的对象所指向的真实类型就是Object数组。
Arrays.copyOf(elementData, size)方法的源代码为
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
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;
}
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
观察发现,当不指定拷贝的数据类型的时候,copyOf将直接使用数据源的数据类型进行对象的拷贝。
查看ArrayList的源代码可以看到elementData的定义为
private transient Object[] elementData;因此,toArray()方法返回的对象的真实类型为Object[ ],因此无法将类型强制转化为String [],即便是ArrayList使用了泛型,也不能进行类似的强制类型转换。
我们一般通过toArray(T[] contents)方法将ArrayList转化为一个目标类型的数组
//toArray(T[] contents)调用方式一
public static Integer[] vectorToArray1(ArrayList<Integer> v) {
Integer[] newText = new Integer[v.size()];
v.toArray(newText);
return newText;
}
//toArray(T[] contents)调用方式二。最常用!
public static Integer[] vectorToArray2(ArrayList<Integer> v) {
Integer[] newText = (Integer[])v.toArray(new Integer[0]);
return newText;
}
//toArray(T[] contents)调用方式三
public static Integer[] vectorToArray3(ArrayList<Integer> v) {
Integer[] newText = new Integer[v.size()];
Integer[] newStrings = (Integer[])v.toArray(newText);
return newStrings;
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
return (T[]) Arrays.copyOf(elementData, size, a.getClass()); # 方法二
System.arraycopy(elementData, 0, a, 0, size); # 方法一和方法三
if (a.length > size)
a[size] = null;
return a;
}
其中,第一种方法和第三种方法都是利用了toArray(T[] contents)方法中的System.arraycopy语句进行数组的拷贝。由于System.arraycopy直接改变了
目标数组引用的内容,因此可以像方法一一样,不需要定义toArray方法的返回值,再将返回值返回,而是可以直接返回目标数组。
方法二则是使用了toArray(T[] contents)方法中Array.copyOf语句重新创建了一个目标类型的数组。
由于方法二的语句更加简洁,因此方法二更为常用。