原文: 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()返回的
并不是Integer[],为什么呢?也许许多人知道答案,这就是
Type Erasure。基本上,编译器在编译过程中会去除一切关于泛型的任何类型。当程序运行时,所有的 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() 来指定要创建数组的类型。
这就是为什么它需要一个参数--为了知道要创建数组的类型。然后你也许会说通过 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 方法。
分享到:
相关推荐
array.zip<br>A simple program that shows how a two-dimensional array works within a VB program.<END><br>70,Bubblesort.zip<br>A simple Bubble Sort code that shows how the program works within a VB ...
- Each node in the list contains a pointer to the `CImg<T>` object and other necessary information for managing the list. ##### 3. `CImgDisplay` **Structure Overview:** - `CImgDisplay` is used for ...
Adding an Entry to a Dictionary Recipe 4.11. Building a Dictionary Without Excessive Quoting Recipe 4.12. Building a Dict from a List of Alternating Keys and Values Recipe 4.13. Extracting a ...
Value arrays - A container structure to maintain an array of generic values III. Tools Reference glib-mkenums - C language enum description generation utility glib-genmarshal - C code marshaller ...
E_ALL & ~E_NOTICE), a quoted string ("bar"), or a reference to a ; previously set variable or directive (e.g. ${foo}) ; Expressions in the INI file are limited to bitwise operators and parentheses: ...
- **Type Casting**: Converting one type to another, including upcasting and downcasting. - **Array Assignments**: Creating and initializing arrays, including multi-dimensional arrays. #### 4. ...
Converting XML to ADO.NET Data 820 Reading and Writing a DiffGram 822 Serializing Objects in XML 825 Serialization without Source Code Access 833 Summary 836 Chapter 24: Working with Active Directory ...
PEP 372: Adding an Ordered Dictionary to collections PEP 378: Format Specifier for Thousands Separator PEP 389: The argparse Module for Parsing Command Lines PEP 391: Dictionary-Based Configuration...
1.4.1 HardwareOrganization of aSystem . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.4.2 Running the helloProgram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.5 CachesMatter ...