关于Java:将ArrayList 转换为String []数组

Convert ArrayList<String> to String[] array

本问题已经有最佳答案,请猛点这里访问。

我在Android环境中工作,并尝试了以下代码,但似乎不起作用。

1
String [] stockArr = (String[]) stock_list.toArray();

如果我定义如下:

1
String [] stockArr = {"hello","world"};

它起作用了。我有什么东西不见了吗?


像这样使用。

1
2
3
4
5
6
7
8
9
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
String[] arr = list.toArray(new String[list.size()]);


正在发生的是,stock_list.toArray()创建的是Object[],而不是String[],因此类型转换失败1。

正确的代码是:

1
  String [] stockArr = stockList.toArray(new String[stockList.size()]);

甚至

1
  String [] stockArr = stockList.toArray(new String[0]);

有关更多详细信息,请参阅JavaDocs了解List.toArray的两个重载。

后一个版本使用零长度数组来确定结果数组的类型。(令人惊讶的是,这样做要比预先分配…至少,对于最近的Java版本来说。详情请参见https://stackoverflow.com/a/4042464/139985。)

从技术角度来看,这种API行为/设计的原因是,List.toArray()方法的实现没有关于在运行时是什么的信息。它只知道原始元素类型是Object。相反,在另一种情况下,array参数给出了数组的基类型。(如果提供的数组足够大以容纳列表元素,则使用它。否则,将分配一个类型相同、大小较大的新数组,并作为结果返回。)

1——在Java中,EDCOX1×1 }不是与EDCOX1 2Ω赋值兼容的。如果是,那么你可以这样做:

1
2
3
    Object[] objects = new Object[]{new Cat("fluffy")};
    Dog[] dogs = (Dog[]) objects;
    Dog d = dogs[0];     // Huh???

这显然是胡说八道,这就是数组类型一般不兼容赋值的原因。


Java 8中的另一种选择:

1
String[] strings = list.stream().toArray(String[]::new);


我可以看到许多答案显示如何解决问题,但只有斯蒂芬的答案试图解释问题发生的原因,因此我将尝试在此主题上添加更多内容。这是一个可能的原因,为什么EDCOX1 0不改变为EDCOX1,1,泛型软件引入Java。好的。为什么String[] stockArr = (String[]) stock_list.toArray();不起作用?

在爪哇中,仅在编译时存在泛型类型。在运行时,有关泛型类型的信息(如在您的情况下,被删除并替换为Object类型(查看类型擦除)。这就是为什么在运行时,toArray()不知道要使用什么精确类型来创建新数组,所以它使用Object作为最安全的类型,因为每个类都扩展对象,这样它就可以安全地存储任何类的实例。好的。现在的问题是,您不能将Object[]的实例强制转换为String[]

为什么?看看这个例子(假设class B extends A):好的。

1
2
3
//B extends A
A a = new A();
B b = (B)a;

虽然这样的代码会编译,但在运行时我们会看到抛出的ClassCastException,因为引用a所持有的实例实际上不是B类型(或其子类型)。为什么会出现此问题(为什么需要强制转换此异常)?其中一个原因是,B可能有a不具备的新方法/字段,因此,即使持有的实例没有(不支持)这些新成员,也有可能有人试图通过B引用来使用这些新成员。换句话说,我们最终可能会尝试使用不存在的数据,这可能会导致许多问题。因此,为了防止这种情况,JVM抛出异常,并停止进一步的潜在危险代码。好的。

你现在可以问,"那我们为什么不早点停下来呢?为什么涉及这种类型转换的代码甚至是可编译的?编译器不应该停止它吗?".答案是:不,因为编译器无法确定a引用所持有的实例的实际类型,并且有可能持有支持B引用接口的B类实例。看看这个例子:好的。

1
2
3
4
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[]类型好的。

1
2
Object[] arr = new Object[] {"ab","cd" };
String[] arr2 = (String[]) arr;//ClassCastException will be thrown

这里的问题有点不同。现在,我们确信String[]数组不会有其他字段或方法,因为每个数组只支持:好的。

  • []运算符,
  • length备案,
  • 从对象父类型继承的方法,

所以这不是数组接口,这使得它不可能。问题是,Strings旁边的Object[]数组可以存储任何对象(例如Integers),所以有一天我们可能会在没有Integer的情况下尝试调用strArray[i].substring(1,3)这样的方法。好的。

因此,为了确保这种情况永远不会发生,在Java数组引用中只能保存好的。

  • 与引用类型相同的数组实例(引用String[] strArr可以容纳String[])
  • 子类型数组的实例(Object[]可以容纳String[],因为StringObject的子类型,

但不能举行好的。

  • 引用的数组类型的父类型数组(String[]不能容纳Object[])
  • 与引用中的类型无关的类型数组(Integer[]不能容纳String[])

换言之,像这样的事情是可以的好的。

1
2
3
4
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

您可以说,解决此问题的一种方法是在运行时在所有列表元素之间找到最常见的类型,并创建该类型的数组,但在列表的所有元素都是从通用类型派生的一种类型的情况下,这不起作用。看一看好的。

1
2
3
4
//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());

现在最常见的是B,不是a,所以toArray()。好的。

1
A[] arr = elements.toArray();

将返回Bnew B[]的数组。这个数组的问题是,虽然编译器允许您通过向它添加new A()元素来编辑它的内容,但您将得到ArrayStoreException,因为B[]数组只能保存类B或其子类的元素,以确保所有元素都支持B的接口,但a的实例除外。可能没有B的所有方法/字段。所以这个解决方案并不完美。好的。

此问题的最佳解决方案是通过将此类型作为方法参数(如好的。

1
String[] arr = list.toArray(new String[list.size()]);

或好的。

1
String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.

好啊。


正确的方法是:

1
String[] stockArr = stock_list.toArray(new String[stock_list.size()]);

我想补充一下这里的其他好答案,并解释一下如何使用javadocs来回答您的问题。

这里是用于toArray()的javadoc(无参数)。如您所见,此方法返回一个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.

不过,在这个方法的正下方是用于toArray(T[] a)的javadoc。如您所见,此方法返回一个T[],其中T是您传入的数组类型。起初,这看起来像是您在寻找的,但不清楚为什么要传入一个数组(您是在添加数组,将其仅用于类型等)。文档清楚地表明,传递数组的目的本质上是定义要返回的数组类型(这正是您的用例):

public 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 (i.e., the array has more elements than the collection), the
element in the array immediately following the end of the collection
is set to 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.)

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.

当然,要真正理解这两种方法之间的区别,需要了解泛型(如其他答案中所述)。然而,如果你先去javadocs,你通常会找到你的答案,然后自己去看看你还需要学习什么(如果你真的这样做的话)。

另外,请注意,在这里阅读javadocs有助于理解传入数组的结构。虽然这实际上并不重要,但您不应像这样传递空数组:

1
String [] stockArr = stockList.toArray(new String[0]);

因为,从文档中,这个实现检查数组是否足够大以包含集合;否则,它将分配一个大小和类型正确的新数组(使用反射)。当您可以轻松地传递大小时,在创建新数组时不需要额外的开销。

通常情况下,javadocs为您提供了丰富的信息和方向。

嘿,等一下,反射是什么?