先说说ArrayList的toArray
ArrayList提供了一个将List转为数组的一个非常方便的方法toArray。toArray有两个重载的方法:
1.list.toArray();
2.list.toArray(T[] a);
对于第一个重载方法,是将list直接转为Object[] 数组;
第二种方法是将list转化为你所需要类型的数组,当然我们用的时候会转化为与list内容相同的类型。
不明真像的同学喜欢用第一个,是这样写:
ArrayList<String> list=
new
ArrayList<String>();
for
(
int
i =
0
; i <
10
; i++) {
list.add(
""
+i);
}
String[] array= (String[]) list.toArray();
结果一运行,报错:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
原因一看就知道了,不能将Object[] 转化为String[].转化的话只能是取出每一个元素再转化,像这样:
Object[] arr = list.toArray();
for
(
int
i =
0
; i < arr.length; i++) {
String e = (String) arr[i];
System.out.println(e);
}
所以第一个重构方法就不是那么好使了。
实际上,将list世界转化为array的时候,第二种重构方法更方便,用法如下:
String[] array =
new
String[list.size()];
list.toArray(array);
List接口的toArray方法可以把一个集合转化为数组,但是使用不方便,toArray()方法 返回的是一个Object数组,所以需要自行转变;toArray(T[] a)虽然返回的是T类型的数组, 但是还需要传入一个T类型的数组,这也挺麻烦的,我们期望输入的是一个泛型化的List, 这样就能转化为泛型数组了,来看看能不能实现,代码如下:
public static<T> T[] toArray(List<T> list){
T[] t=(T[]) new Object[list.size()];
for(int i=0;i<list.size();i++){
t[i]=list.get(i);
}
return t;
}
上面把要输出的参数类型定义为Object数组,然后转型为T类型数组,之后遍历List 赋值给数组的每个元素,这与ArrayList的toArray方法很类似(注意只是类似),客户端的 调用如下:
List<String> list=Arrays.asList("a","b");
for(String str:toArray(list)){
System.out.println(str);
}
编译没有任何问题,运行后出现如下异常:
Exception in thread"main" java.lang. ClassCastException: [Ljava.lang.Object;
cannot becast to [Ljava.lang.String; at Client.main(Client.java:20)
类型转换异常,也就是说不能把一个Object数组转换为String数组,这段异常包含了两 个问题:
口为什么Object数组不能向下转型为String数组?
数组是一个容器,只有确保容器内的所有元素类型与期望的类型有父子关系时才能转换,Object数组只能保证数组内的元素是Object类型,却不能确保它们都是String的父类型 或子类,所以类型转换失败。
口为什么是main方法抛出异常,而不是toArray方法?
其实,是在toArray方法中进行的类型向下转换,而不是main方法中。那为什么异常会 在main方法中抛出,应该在toArray方法的T[] t = (T[])new Object[list.size]这段代码 才对呀?那是因为泛型是类型擦除的,toArray方法经过编译后与如下代码相同:
//此处的强制类型没必要存在,只是为了与源代码对比
Object [] t = (Object [] ) new Object[list .size ()];
for(inti=0,n=list.size(};i<n;i++){
t[i] = list.get (i);
}
return t;
public static void main(String[]args) {
List<String> list = Arrays.asList("A","B");
for(Stringstr:(String[])toArray(list)){
System.out.println(str);
}
阅读完此段代码就很清楚了 : toArray方法返回后会进行一次类型转换,Object数组转 换成了 String数组,于是就报ClassCastException异常了。
Object数组不能转为String数组,T类型又无法在运行期获得,那该如何解决这个问题 呢?其实,要想把一个Obejct数组转换为String数组,只要Object数组的实际类型(Actual Type)也是String就可以了,例如:
//objArray的实际类型和表面类型都是String数组
Object[] objArray={"a","b"};
String[] strArray=(String[]) objArray;//<span class="postTitle2">// 抛出 ClassCastException</span>
String[] ss={"a","b"};
Object[] objs=ss;
//objs的真实类型是String数组,显示类型为Object数组
//顺利转换为String数组
String[] strs=(String[]) objs;
明白了这个问题,我们就把泛型数组声明为泛型类的子类型吧!代码如下:
public static<T> T[] toArray(List<T> list,Class<T> tClass){
T[] t=(T[]) Array.newInstance(tClass, list.size());
for(int i=0;i<list.size();i++){
t[i]=list.get(i);
}
return t;
}
List<String> list=Arrays.asList("a","b");
for(String str:toArray(list,String.class)){
System.out.println(str);
}
通过反射类Array声明了一个T类型的数组,由于我们无法在运行期获得泛型类型的参 数,因此就需要调用者主动传入T参数类型。此时,客户端再调用就不会出现任何异常了。
在这里我们看到,当一个泛型类(特别是泛型集合)转变为泛型数组时,泛型数组的真实类型不能是泛型类型的父类型(比如顶层类Object),只能是泛型类型的子类型(当然包