我在Android环境中工作并尝试过以下代码,但它似乎没有用。
String [] stockArr = (String[]) stock_list.toArray();
如果我定义如下:
String [] stockArr = {"hello", "world"};
它有效。有什么东西我不见了吗?
答案 0 :(得分:1695)
像这样使用。
List<String> stockList = new ArrayList<String>();
stockList.add("stock1");
stockList.add("stock2");
String[] stockArr = new String[stockList.size()];
stockArr = stockList.toArray(stockArr);
for(String s : stockArr)
System.out.println(s);
答案 1 :(得分:910)
试试这个
String[] arr = list.toArray(new String[list.size()]);
答案 2 :(得分:296)
正在发生的事情是stock_list.toArray()
正在创建Object[]
而不是String[]
,因此类型转换失败 1 。
正确的代码是:
String [] stockArr = stockList.toArray(new String[stockList.size()]);
甚至
String [] stockArr = stockList.toArray(new String[0]);
有关更多详细信息,请参阅List.toArray
的两个重载的javadoc。
后一版本使用零长度数组来确定结果数组的类型。 (令人惊讶的是,执行此操作要比预先分配...更快,对于最近的Java版本。请参阅https://stackoverflow.com/a/4042464/139985了解详细信息。)
从技术角度来看,此API行为/设计的原因是List<T>.toArray()
方法的实现没有<T>
在运行时的信息。它只知道原始元素类型是Object
。相反,在另一种情况下,数组参数给出了数组的基本类型。 (如果提供的数组足够大以容纳列表元素,则使用它。否则,将分配相同类型和更大大小的新数组并作为结果返回。)
1 - 在Java中,Object[]
与String[]
的分配不兼容。如果是,那么你可以这样做:
Object[] objects = new Object[]{new Cat("fluffy")};
Dog[] dogs = (Dog[]) objects;
Dog d = dogs[0]; // Huh???
这显然是无稽之谈,这就是为什么数组类型通常不兼容分配的原因。
答案 3 :(得分:143)
Java 8中的替代方案:
String[] strings = list.stream().toArray(String[]::new);
答案 4 :(得分:84)
我可以看到许多答案显示如何解决问题,但只有Stephen's answer试图解释为什么会出现问题所以我会尝试在这个主题上添加更多内容。这是一个关于Object[] toArray
未被更改为T[] toArray
的可能原因的故事,其中泛型商品被引入Java。
String[] stockArr = (String[]) stock_list.toArray();
不起作用?在Java中, 泛型类型仅在编译时存在 。在运行时,有关泛型类型的信息(如您的情况<String>
)将被删除并替换为Object
类型(请查看type erasure)。这就是为什么在运行时toArray()
不知道用于创建新数组的精确类型,因此它使用Object
作为最安全的类型,因为每个类都扩展了Object,因此它可以安全地存储任何类的实例。
Object[]
的实例投射到String[]
。为什么呢?看一下这个例子(假设class B extends A
):
//B extends A
A a = new A();
B b = (B)a;
虽然这样的代码会编译,但在运行时我们会看到抛出ClassCastException
,因为引用a
持有的实例实际上并不是B
类型(或其子类型)。为什么会出现这个问题(为什么需要抛出此异常)?其中一个原因是B
可能有A
没有的新方法/字段,因此有人可能会尝试通过b
引用来使用这些新成员即使持有实例没有(不支持)它们。换句话说,我们最终可能会尝试使用不存在的数据,这可能会导致许多问题。因此,为了防止这种情况,JVM抛出异常,并停止进一步的潜在危险代码。
你现在可以问“那么为什么我们不能更早地停止?为什么涉及这种投射的代码甚至可以编译?不应该编译器阻止它吗?”。答案是:不,因为编译器不能确定a
引用所持有的实例的实际类型是什么,并且它有可能保存类B
的实例,它将支持接口b
参考。看一下这个例子:
A a = new B();
// ^------ Here reference "a" holds instance of type B
B b = (B)a; // so now casting is safe, now JVM is sure that `b` reference can
// safely access all members of B class
现在让我们回到你的阵列。如您所见,我们无法将Object[]
数组的实例转换为更精确的类型String[]
,如
Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;//ClassCastException will be thrown
这里的问题有点不同。现在我们确定String[]
数组不会有其他字段或方法,因为每个数组仅支持:
[]
运营商,length
提交,所以不是数组接口使得它变得不可能。问题是Object[]
旁边的 Strings
数组可以存储任何对象 (例如Integers
)所以有可能是一个美好的一天我们最终会尝试在没有这种方法的strArray[i].substring(1,3)
实例上调用Integer
之类的方法。
为了使确定这种情况永远不会发生,在Java数组引用中只能保存
String[] strArr
可以容纳String[]
)Object[]
可以容纳String[]
,因为String
是Object
的子类型,但无法举行
String[]
无法容纳Object[]
)Integer[]
无法容纳String[]
)换句话说,这样就可以了
Object[] arr = new String[] { "ab", "cd" }; //OK - because
// ^^^^^^^^ `arr` holds array of subtype of Object (String)
String[] arr2 = (String[]) arr; //OK - `arr2` reference will hold same array of same type as
// reference
你可以说解决这个问题的一种方法是在运行时找到所有列表元素之间最常见的类型并创建该类型的数组,但这不适用于列表的所有元素都是派生自的一种类型的情况通用的。看看
//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());
现在最常见的类型是B
,而不是A
所以toArray()
A[] arr = elements.toArray();
将返回B
类new B[]
的数组。此数组的问题在于,虽然编译器允许您通过向其添加new A()
元素来编辑其内容,但您将获得ArrayStoreException
,因为B[]
数组只能包含类{{1}的元素或者它的子类,以确保所有元素都支持B
的接口,但B
的实例可能没有A
的所有方法/字段。所以这个解决方案并不完美。
这个问题的最佳解决方案是通过将此类型作为方法参数传递来明确告诉应该返回什么类型的数组B
,如
toArray()
或
String[] arr = list.toArray(new String[list.size()]);
答案 5 :(得分:20)
正确的方法是:
String[] stockArr = stock_list.toArray(new String[stock_list.size()]);
我想在这里添加其他很好的答案并解释你如何使用Javadocs来回答你的问题。
toArray()
(无参数)的Javadoc是here。如您所见,此方法返回Object[]
和 not String[]
,它是列表的运行时类型的数组:
public Object[] toArray()
返回包含所有内容的数组 此集合中的元素。如果该集合作出任何保证 它的迭代器返回它的元素的顺序,这个方法 必须以相同的顺序返回元素。返回的数组将是 “安全”,因为该集合没有保留对它的引用。 (换句话说,即使这个方法必须分配一个新的数组 集合由Array支持。因此呼叫者可以自由修改 返回的数组。
在该方法的正下方,toArray(T[] a)
为the Javadoc。正如您所看到的,此方法返回T[]
,其中T
是您传入的数组的类型。首先,这似乎就是您要查找的内容,但目前还不清楚为什么您要这么做传入一个数组(你是添加它,只使用它类型等)。该文档清楚地表明,传递数组的目的主要是定义要返回的数组类型(这正是您的用例):
public <T> T[] toArray(T[] a)
返回包含所有内容的数组 这个系列中的元素; 返回数组的运行时类型 指定数组的那个。如果集合适合指定的数组 数组,它返回其中。否则,将分配一个新数组 使用指定数组的运行时类型和大小 采集。如果集合适合具有空间的指定数组 备用(即,数组有比集合更多的元素),. 紧接在集合结束之后的数组中的元素 设置为null。这在确定长度时非常有用 仅当调用者知道集合不知道时才收集 包含任何null元素。)
如果此集合对其元素的顺序做出任何保证 由迭代器返回,此方法必须返回元素 同样的订单。
此实现检查数组是否足够大以包含 采集;如果没有,它会分配一个正确大小的新数组 类型(使用反射)。然后,它遍历集合, 将每个对象引用存储在下一个连续元素中 数组,从元素0开始。如果数组大于 集合,null结束后存储在第一个位置 集合。
当然,需要了解泛型(如其他答案中所述)才能真正理解这两种方法之间的区别。然而,如果你第一次去Javadocs,你通常会找到你的答案,然后亲自看看你还需要学习什么(如果你真的这样做)。
另请注意,在此处阅读Javadoc可帮助您了解传入的数组的结构应该是什么。虽然它实际上可能并不重要,但你不应该像这样传入一个空数组:
String [] stockArr = stockList.toArray(new String[0]);
因为,从doc,这个实现检查数组是否足够大以包含集合; 如果没有,它会分配一个正确大小和类型的新数组(使用反射)。当你可以轻松传入大小时,不需要额外的开销来创建一个新数组。
通常情况下,Javadocs为您提供了丰富的信息和方向。
嘿等一下,有什么反映?