本文翻译自:Convert ArrayList to String[] array [duplicate]
This question already has an answer here: 这个问题已经在这里有了答案:
I'm working in the android environment and have tried the following code, but it doesn't seem to be working. 我在android环境中工作,并尝试了以下代码,但似乎不起作用。
String [] stockArr = (String[]) stock_list.toArray();
If I define as follows: 如果我定义如下:
String [] stockArr = {"hello", "world"};
it works. 有用。 Is there something that I'm missing? 有什么我想念的吗?
#1楼
参考:https://stackoom.com/question/MY6R/转换ArrayList-String-到String-数组-重复
#2楼
I can see many answers showing how to solve problem, but only Stephen's answer is trying to explain why problem occurs so I will try to add something more on this subject. 我可以看到许多显示如何解决问题的答案 ,但是只有Stephen的答案试图解释为什么会出现问题,因此我将尝试在此主题上添加更多内容。 It is a story about possible reasons why Object[] toArray
wasn't changed to T[] toArray
where generics ware introduced to Java. 这是一个有关为什么将Object[] toArray
不更改为T[] toArray
可能原因的故事,在Java中引入了泛型商品。
Why String[] stockArr = (String[]) stock_list.toArray();
为什么String[] stockArr = (String[]) stock_list.toArray();
wont work? 不会工作吗?
In Java, generic type exists at compile-time only . 在Java中, 泛型类型仅在编译时存在 。 At runtime information about generic type (like in your case <String>
) is removed and replaced with Object
type (take a look at type erasure ). 在运行时,有关泛型类型(例如您的<String>
)的信息将被删除,并替换为Object
类型(请查看type erasure )。 That is why at runtime toArray()
have no idea about what precise type to use to create new array, so it uses Object
as safest type, because each class extends Object so it can safely store instance of any class. 这就是为什么在运行时toArray()
不知道使用哪种精确类型来创建新数组的原因,所以它使用Object
作为最安全的类型,因为每个类都扩展Object以便可以安全地存储任何类的实例。
Now the problem is that you can't cast instance of Object[]
to String[]
. 现在的问题是您不能将Object[]
实例转换为String[]
。
Why? 为什么? Take a look at this example (lets assume that class B extends A
): 看一下这个例子(假设class B extends A
):
//B extends A
A a = new A();
B b = (B)a;
Although such code will compile, at runtime we will see thrown ClassCastException
because instance held by reference a
is not actually of type B
(or its subtypes). 尽管将编译此类代码,但在运行时我们会看到抛出ClassCastException
因为引用a
持有的实例实际上不是B
型(或其子类型)。 Why is this problem (why this exception needs to be cast)? 为什么会出现此问题(为什么需要强制抛出此异常)? One of the reasons is that B
could have new methods/fields which A
doesn't, so it is possible that someone will try to use these new members via b
reference even if held instance doesn't have (doesn't support) them. 原因之一是B
可能拥有A
没有的新方法/字段,因此即使持有的实例不具备(不支持)它们,有人也可能会尝试通过b
引用使用这些新成员。 。 In other words we could end up trying to use data which doesn't exist, which could lead to many problems. 换句话说,我们最终可能会尝试使用不存在的数据,这可能会导致许多问题。 So to prevent such situation JVM throws exception, and stop further potentially dangerous code. 因此,为防止这种情况,JVM抛出异常,并停止进一步的潜在危险代码。
You could ask now "So why aren't we stopped even earlier? Why code involving such casting is even compilable? Shouldn't compiler stop it?". 您现在可以问:“为什么我们不更早停止?为什么涉及这种转换的代码甚至可以编译?编译器不应该停止它吗?”。 Answer is: no because compiler can't know for sure what is the actual type of instance held by a
reference, and there is a chance that it will hold instance of class B
which will support interface of b
reference. 答案是:不能因为编译器不能肯定知道是持有实例的实际类型a
参考,并有机会,它会持有类的实例B
将支持的接口b
参考。 Take a look at this example: 看一下这个例子:
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
Now lets go back to your arrays. 现在让我们回到您的阵列。 As you see in question, we can't cast instance of Object[]
array to more precise type String[]
like 如您所见,我们无法将Object[]
数组的实例转换为更精确的String[]
类型,例如
Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;//ClassCastException will be thrown
Here problem is a little different. 这里的问题有些不同。 Now we are sure that String[]
array will not have additional fields or methods because every array support only: 现在我们确定String[]
数组将没有其他字段或方法,因为每个数组仅支持:
-
[]
operator,[]
运算符, -
length
filed,length
, - methods inherited from Object supertype, 从对象超类型继承的方法,
So it is not arrays interface which is making it impossible. 因此, 不是数组接口是不可能的。 Problem is that Object[]
array beside Strings
can store any objects (for instance Integers
) so it is possible that one beautiful day we will end up with trying to invoke method like strArray[i].substring(1,3)
on instance of Integer
which doesn't have such method. 问题是Strings
旁边的Object[]
数组可以存储任何对象 (例如Integers
),因此有一天我们可能会尝试在实例上调用诸如strArray[i].substring(1,3)
类的方法来结束美好的一天。没有这种方法的Integer
。
So to make sure that this situation will never happen, in Java array references can hold only 因此,为了确保这种情况永远不会发生,在Java数组中引用只能包含
- instances of array of same type as reference (reference
String[] strArr
can holdString[]
) 与引用相同类型的数组的实例(引用String[] strArr
可以容纳String[]
) - instances of array of subtype (
Object[]
can holdString[]
becauseString
is subtype ofObject
), 子类型数组的实例(Object[]
可以容纳String[]
因为String
是Object
子类型),
but can't hold 但不能容纳
- array of supertype of type of array from reference (
String[]
can't holdObject[]
) 来自引用的数组类型的超类型数组(String[]
无法容纳Object[]
) - array of type which is not related to type from reference (
Integer[]
can't holdString[]
) 与引用类型无关的类型数组(Integer[]
不能容纳String[]
)
In other words something like this is OK 换句话说,这样就可以了
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
You could say that one way to resolve this problem is to find at runtime most common type between all list elements and create array of that type, but this wont work in situations where all elements of list will be of one type derived from generic one. 您可以说解决此问题的一种方法是在运行时在所有列表元素之间找到最常见的类型,并创建该类型的数组,但这在列表的所有元素都是从通用类派生的一种类型的情况下不起作用。 Take a look 看一看
//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());
now most common type is B
, not A
so toArray()
现在最常见的类型是B
,而不是A
所以toArray()
A[] arr = elements.toArray();
would return array of B
class new B[]
. 将返回B
类new B[]
数组。 Problem with this array is that while compiler would allow you to edit its content by adding new A()
element to it, you would get ArrayStoreException
because B[]
array can hold only elements of class B
or its subclass, to make sure that all elements will support interface of B
, but instance of A
may not have all methods/fields of B
. 该数组的问题在于,尽管编译器允许您通过向其中添加new A()
元素来编辑其内容,但由于B[]
数组仅可容纳类B
或其子类的元素,因此要确保所有内容,您都会得到ArrayStoreException
元素将支持B
接口,但A
实例可能不具有B
所有方法/字段。 So this solution is not perfect. 因此,此解决方案并不完美。
Best solution to this problem is explicitly tell what type of array toArray()
should be returned by passing this type as method argument like 解决此问题的最佳解决方案是通过将此类型作为方法参数传递,来明确告知应返回哪种类型的数组toArray()
String[] arr = list.toArray(new String[list.size()]);
or 要么
String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.
#3楼
The correct way to do this is: 正确的方法是:
String[] stockArr = stock_list.toArray(new String[stock_list.size()]);
I'd like to add to the other great answers here and explain how you could have used the Javadocs to answer your question. 我想在这里添加其他出色的答案,并说明您如何使用Javadocs回答您的问题。
The Javadoc for toArray()
(no arguments) is here . toArray()
的Javadoc(无参数)在此处 。 As you can see, this method returns an Object[]
and not String[]
which is an array of the runtime type of your list: 如您所见,此方法返回一个Object[]
而不是 String[]
,它是列表的运行时类型的数组:
public Object[] toArray()
Returns an array containing all of the elements in this collection. 返回一个包含此集合中所有元素的数组。 If the collection makes any guarantees as to what order its elements are returned by its iterator, this method must return the elements in the same order. 如果集合保证其迭代器返回其元素的顺序,则此方法必须以相同的顺序返回元素。 The returned array will be "safe" in that no references to it are maintained by the collection. 返回的数组将是“安全的”,因为集合不会保留对其的引用。 (In other words, this method must allocate a new array even if the collection is backed by an Array). (换句话说,即使该集合由数组支持,此方法也必须分配一个新数组)。 The caller is thus free to modify the returned array. 因此,调用者可以自由修改返回的数组。
Right below that method, though, is the Javadoc for toArray(T[] a)
. 但是,在该方法的正下方是toArray(T[] a)
的Javadoc 。 As you can see, this method returns a T[]
where T
is the type of the array you pass in. At first this seems like what you're looking for, but it's unclear exactly why you're passing in an array (are you adding to it, using it for just the type, etc). 如您所见,此方法返回一个T[]
,其中T
是您要传递的数组的类型。起初,这看起来像您要查找的内容,但不清楚为什么要传递数组(您添加到其中,仅将其用于类型等)。 The documentation makes it clear that the purpose of the passed array is essentially to define the type of array to return (which is exactly your use case): 该文档清楚地表明,传递的数组的目的实质上是定义要返回的数组的类型(这正是您的用例):
public <T> T[] toArray(T[] a)
Returns an array containing all of the elements in this collection; 返回包含此集合中所有元素的数组; the runtime type of the returned array is that of the specified array. 返回数组的运行时类型是指定数组的运行时类型。 If the collection fits in the specified array, it is returned therein. 如果集合适合指定的数组,则将其返回其中。 Otherwise, a new array is allocated with the runtime type of the specified array and the size of this collection. 否则,将使用指定数组的运行时类型和此集合的大小分配一个新数组。 If the collection fits in the specified array with room to spare (ie, the array has more elements than the collection), the element in the array immediately following the end of the collection is set to null. 如果集合适合指定的数组且有剩余空间(即,数组中的元素多于集合),则紧接集合结束后的数组中的元素将设置为null。 This is useful in determining the length of the collection only if the caller knows that the collection does not contain any null elements.) 仅当调用者知道集合不包含任何null元素时,这才对确定集合的长度很有用。)
If this collection makes any guarantees as to what order its elements are returned by its iterator, this method must return the elements in the same order. 如果此集合对由其迭代器返回其元素的顺序做出任何保证,则此方法必须按相同顺序返回元素。
This implementation checks if the array is large enough to contain the collection; 此实现检查数组是否足够大以包含集合; if not, it allocates a new array of the correct size and type (using reflection). 如果不是,它将分配正确大小和类型的新数组(使用反射)。 Then, it iterates over the collection, storing each object reference in the next consecutive element of the array, starting with element 0. If the array is larger than the collection, a null is stored in the first location after the end of the collection. 然后,对集合进行迭代,将每个对象引用存储在数组的下一个连续元素(从元素0开始)中。如果数组大于集合,则在集合结束后的第一个位置存储一个null。
Of course, an understanding of generics (as described in the other answers) is required to really understand the difference between these two methods. 当然,要真正理解这两种方法之间的区别,需要了解泛型(如其他答案所述)。 Nevertheless, if you first go to the Javadocs, you will usually find your answer and then see for yourself what else you need to learn (if you really do). 但是,如果您先去Javadocs,通常会找到答案,然后亲自了解还需要学习什么(如果确实如此)。
Also note that reading the Javadocs here helps you to understand what the structure of the array you pass in should be. 还要注意,在这里阅读Javadocs可以帮助您了解传入的数组的结构。 Though it may not really practically matter, you should not pass in an empty array like this: 尽管实际上可能并不重要,但您不应传递这样的空数组:
String [] stockArr = stockList.toArray(new String[0]);
Because, from the doc, this implementation checks if the array is large enough to contain the collection; 因为此实现从文档中检查数组是否足够大以包含集合;因此,请执行以下操作: if not, it allocates a new array of the correct size and type (using reflection). 如果不是,它将分配正确大小和类型的新数组(使用反射)。 There's no need for the extra overhead in creating a new array when you could easily pass in the size. 当您可以轻松传递大小时,在创建新数组时不需要额外的开销。
As is usually the case, the Javadocs provide you with a wealth of information and direction. 通常,Javadocs为您提供大量信息和指导。
Hey wait a minute, what's reflection? 嘿,等一下,这反映了什么?
#4楼
Java 8中的替代方法:
String[] strings = list.stream().toArray(String[]::new);
#5楼
尝试这个
String[] arr = list.toArray(new String[list.size()]);
#6楼
What is happening is that stock_list.toArray()
is creating an Object[]
rather than a String[]
and hence the typecast is failing 1 . 发生的情况是stock_list.toArray()
正在创建Object[]
而不是String[]
,因此类型转换失败1 。
The correct code would be: 正确的代码是:
String [] stockArr = stockList.toArray(new String[stockList.size()]);
or even 甚至
String [] stockArr = stockList.toArray(new String[0]);
For more details, refer to the javadocs for the two overloads of List.toArray
. 有关更多详细信息,请参阅javadocs以获取List.toArray
的两个重载。
The latter version uses the zero-length array to determine the type of the result array. 后者使用零长度数组来确定结果数组的类型。 (Surprisingly, it is faster to do this than to preallocate ... at least, for recent Java releases. See https://stackoverflow.com/a/4042464/139985 for details.) (令人惊讶的是,至少在最近的Java版本中,这样做比预分配要快得多。有关详细信息,请参阅https://stackoverflow.com/a/4042464/139985 。)
From a technical perspective, the reason for this API behavior / design is that an implementation of the List<T>.toArray()
method has no information of what the <T>
is at runtime. 从技术角度来看,此API行为/设计的原因是List<T>.toArray()
方法的实现不包含<T>
在运行时的信息。 All it knows is that the raw element type is Object
. 它所知道的只是原始元素类型是Object
。 By contrast, in the other case, the array parameter gives the base type of the array. 相反,在另一种情况下,array参数给出了数组的基本类型。 (If the supplied array is big enough to hold the list elements, it is used. Otherwise a new array of the same type and a larger size is allocated and returned as the result.) (如果提供的数组大到足以容纳列表元素,则使用它。否则,将分配相同类型和更大尺寸的新数组并作为结果返回。)
1 - In Java, an Object[]
is not assignment compatible with a String[]
. 1-在Java中, Object[]
与String[]
不兼容。 If it was, then you could do this: 如果是这样,则可以执行以下操作:
Object[] objects = new Object[]{new Cat("fluffy")};
Dog[] dogs = (Dog[]) objects;
Dog d = dogs[0]; // Huh???
This is clearly nonsense, and that is why array types are not generally assignment compatible. 这显然是胡说八道,这就是为什么数组类型通常不兼容分配的原因。