将字符串列表转换为字符串数组时的不同行为

时间:2016-06-24 19:28:07

标签: java arrays arraylist collections

我正在使用Java中的Collection框架,在那里我遇到了一个奇怪的问题。 我在ArrayList的帮助下制作了2个Strings 1列表,而第二个是使用Arrays.asList(T ...)制作的。

创建这两个列表后,我尝试将这些列表转换为带有list.toArray()的字符串数组,
当list.toArray()方法调用返回一个对象数组时,所以我不得不显式地转换为String []。

在施放之后发生了一些奇怪的行为:

案例#1 :(使用ArrayList创建列表的位置),将运行时异常作为 java.lang.ClassCastException:[Ljava.lang.Object;无法转换为[Ljava.lang.String;

案例2 :(使用Arrays.asList(T ...)创建的列表)运行正常。

这是代码

String [] str = null ,str1 = null ;
List<String> list = new ArrayList<String>();
list.add("a");
List<String> list1 = Arrays.asList("a");
str = (String[]) list.toArray();  // Runtime Exception 
str1 = (String[]) list1.toArray(); // Runs Fine 

4 个答案:

答案 0 :(得分:7)

ArrayListObject[]支持。使用toArray()返回该数组的副本。

  

返回一个包含此列表中所有元素的数组   序列(从第一个元素到最后一个元素)。

它不保证返回的数组类型。但我们从异常的消息中知道这一点。如果您希望它返回String[],请使用为此提供的重载方法。

 str = list.toArray(new String[0]);  

演员阵容变得不必要了。

Arrays.asList返回的List实现维护对作为其变量arity参数传递的数组(隐式或显式)的引用。

  

返回由指定数组支持的固定大小的列表。

调用

List<String> list1 = Arrays.asList("a");

创建一个String[]并将其作为参数传递给asList。 API文档没有明确说明这一点,但支持的似乎表明它将返回相同的数组。 (查看实现,它返回一个克隆。)因为它是String[],所以在转换并将其分配给该类型的变量时没有错误。

在这两种情况下,适当的解决方案是使用重载的List#toArray(T[])

为了好玩,请运行以下命令并检查返回的数组类型。

List<String> list1 = (List) Arrays.<Object> asList("a");
System.out.println(list1.toArray().getClass());

不要做出假设。始终依赖API文档。如果不清楚,请尝试找到更好的解决方案。

答案 1 :(得分:4)

toArray的不同调用返回具有不同组件类型的数组。您可以通过运行以下代码来查看:

    List<String> list = new ArrayList<String>();
    list.add("a");
    List<String> list1 = Arrays.asList("a");
    System.out.println(list.toArray().getClass());
    System.out.println(list1.toArray().getClass());

在任何版本的Java 8或更早版本上,结果为

    class [Ljava.lang.Object;
    class [Ljava.lang.String;

基本上,此输出分别表示Object[]String[]

然而,这是一个错误。见JDK-6260652。虽然没有明确说明,Collection.toArray() 必须返回Object[]而不是Object的某个子类型的数组。这有几个原因。

首先是在JDK 1.2中引入Collection.toArray(),早在将泛型添加到该语言之前。除了Object[]之外,任何集合实现都不可能返回任何其他内容,因此为了兼容性,所有集合的toArray()实现都必须返回Object[]

第二个原因是toArray(T[])规范中的一个相当随意的评论说:

  

请注意,toArray(new Object [0])在功能上与toArray()相同。

再次要求toArray()返回Object[]而不是其他类型的数组。

此错误已在JDK 9中修复。在最近的JDK 9版本上运行上面的代码片段会提供以下输出:

    class [Ljava.lang.Object;
    class [Ljava.lang.Object;

Arrays.asList("a")使用String[]进行内部存储的事实是一个实现细节。 toArray()返回Object[]以外的其他内容的错误是此实现细节泄漏。 (实际上,数组是由varargs机制创建的,使用方法的类型参数作为数组组件类型。Arrays.asList()只包装给定的数组。)

正如其他人所说,如果你想控制返回数组的组件类型,请使用另一个重载toArray(T[])

    String[] array = list.toArray(new String[0]);
    String[] array1 = list1.toArray(new String[0]);

答案 2 :(得分:1)

List<String> list = new ArrayList<String>();
list.add("a");
str = (String[]) list.toArray();

在这种情况下,您的列表调用ArrayList类的方法toArray()。如下所示,它返回Object []:

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

并且elementData声明:

transient Object[] elementData;

构造函数方法:

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

和DEFAULTCAPACITY_EMPTY_ELEMENTDATA:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

因为,elementData是totaly Object []并且不能被转换为任何类型,String等......

使用Arrays.asList(T ...),它返回java.util.Arrays $ ArrayList类。而java.util.Arrays $ ArrayList也有toArray()方法。那种微妙的toArray()方法让人有些迷惑:)。这是它的实现:

public Object[] toArray() {
    return a.clone();
}

最后一个字段声明:

private final E[] a;

java.util.Arrays $ ArrayList.toArray()能够返回Object []并实际返回E []。希望这会对你有所帮助:)。

答案 3 :(得分:0)

此处的关键是Arrays.asList(..)不返回java.util.ArrayList,而是返回java.util.Arrays$ArrayList。因此.toArray()方法略有不同。

如果您希望第一种情况返回String [],则可以将调用更改为

str = list.toArray(new String[0]);