我正在使用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
答案 0 :(得分:7)
ArrayList
由Object[]
支持。使用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]);