Converting a Collection<T> to an array

原文: http://www.javablogging.com/converting-a-collection-to-an-array/

如果你已经使用了java5的Collections有可能被Collection<T>输出数组类型为T的问题难倒。在Collection Interface中有方法 toArray() 返回 Object[] 类型。但是,如果java5的Collection使用泛型,难倒不应该得到一个array T[] 使用在我们的Collection<T>的声明中?当然有一个方法 T[] toArray(T[] a)。但是哪来的这个奇怪的“T[] a”?为什么不是一个简单的没有奇怪参数的方法 T[] toArray(T[] a)?看起来在java5中实现这样一个方法不应该有大的问题,但是事实上不肯能做到在java5,我们在下面说明为什么。

我们从尝试自己实现这样一个方法开始,例如,我们可以通过继承 ArrayList 类来实现方法T[] toArrayT()。为了简单我们通过强制转换 Object[] toArray() 方法为T[]来实现:




import java.util.*;

/**
* Implementation of the List interface with "T[] toArrayT()" method.
* In a normal situation you should never extend ArrayList like this !
* We are doing it here only for the sake of simplicity.
* @param <T> Type of stored data
*/
class HasToArrayT<T> extends ArrayList<T> {
public T[] toArrayT() {
return (T[]) toArray();
}
}

public class Main {
public static void main(String[] args) {
// We create an instance of our class and add an Integer
HasToArrayT<Integer> list = new HasToArrayT<Integer>();
list.add(new Integer(4));

// We have no problems using the Object[] toArray method
Object[] array = list.toArray();
System.out.println(array[0]);

// Here we try to use our new method... but fail on runtime
Integer[] arrayT = list.toArrayT(); // Exception is thrown :
// "Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer"
System.out.println(arrayT[0]);
}
}


如果你编译这段代码,当我们尝试使用这个新方法它会抛出一个 exception。但是为什么呢?但是为什么?难倒是我们做的不对?存储是Integers 和 toArray 方法返回的Objects数组,所以这才是问题所在。

Java 编译器 给我们一个提示来理解我们的错误。通过编译你将看到第11行的warning,说一些什么“Type Safety”。 Unchecked cast 从 Object[] 到 T[],跟随这个问题检查这个 array 从方法 toArrayT() 返回的真正类型是 Integer[],把下面的代码放在main()中代码
Integer[] arrayT = list.toArrayT()
之前:


System.out.println("toArrayT() returns Integer[] : "
+ (list.toArrayT() instanceof Integer[]));


当你运行这个程序,你应该得到一个"false",这意味着toArrayT()返回的[b]并不是[/b]Integer[],为什么呢?也许许多人知道答案,这就是[url=http://java.sun.com/docs/books/tutorial/java/generics/erasure.html]Type Erasure[/url]。基本上,编译器在编译过程中会去除一切关于泛型的任何类型。当程序运行时,所有的 T 在 HasToArrayT类 中的表象并不是一个简单的对象。这意味着,方法toArrayT 并没有转换成Integer[],即使泛型中定义的为Integer。其实这里还有第二个问题,来自java处理数组转换的方式。现在我们只想集中在第一个问题--泛型的使用。

所以,如果 T 代表 Integer 却会在运行期擦除,有没有什么方法返回真正的array of type Integer[]?当然可以而且是已经存在的,方法 T[] toArray(T[] a)在Collections框架就是这个作用。但是代价就是添加了一个自己创建的参数实例。怎么使用它呢?让我们看看它们是怎么实现的?例如,ArrayList的实现:


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


按照javadoc中的这个方法的解释,如果这个制定的数组 a 有足够大的空间满足elements放在这个数组中,如果没有足够空间将会创建一个新的数组。我们看到实现中它通过Arrays.copyOf 方法创建一个新的数组,通过 a.getClass() 来指定要创建数组的类型。[b]这就是为什么它需要一个参数--为了知道要创建数组的类型。[/b]然后你也许会说通过 T.class 替代 a.getClass()会不会显得更简单?不!因为type erasure,T不是一个我们指定的一个类型。而且我们如果我们使用T.class会得到一个编译器错误!也许你想到一个创建新数组的简单方法 -- new T[], 但是由于同样的原因仍然不可能。

直接的说,为了返回一个真正指定类型的array of T,必须为函数 toArray 提供相应的类型。现在并没有其他的方式可以提供函数中创建的数组类型,由于泛型 T 在Collection运行期创建时被取消。

另外一个途径就是通过传递一个 Class<T[]> 输入参数实现这个函数,这种方式通过创建一个新的数组T[]替代 toArray(a) 中的 a, 我们可以用 .class 域 -- toArrayT(TYPE[].class)


public T[] toArrayT(Class<T[]> c) {
return (T[]) Arrays.copyOf(toArray(), size(), c);
}


它仍然需要一个参数,但是这有两个好处在T[] toArray(T[] a)函数。首先,它不需要另外常见一个数组而是直接使用传递来的实例。第二,它简单,当然它的确定也是有一个传入参数。

总之,没有完美的解决办法,最好的解决办法是不使用数组。数组代码很难控制并且导致人去犯错并出现在运行的时候。如果你经常使用collections 并且熟悉,你想不能逃避关于注意 toArray 方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值