java - to数组 - 数组初始化arraylist




将ArrayList<String>转换为String[] array (4)

Java 8中的另一种选择:

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

这个问题在这里已经有了答案:

我正在android环境中工作,并尝试了下面的代码,但它似乎并没有工作。

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

如果我定义如下:

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

有用。 有什么我失踪了吗?


我可以看到许多答案显示如何解决问题,但只有斯蒂芬的答案试图解释为什么会出现问题,所以我会尝试在此主题上添加更多内容。 这是一个关于为什么Object[] toArray没有改变为T[] toArray可能原因的故事,其中泛型引入了Java。

为什么String[] stockArr = (String[]) stock_list.toArray(); 不会工作?

在Java中, 泛型类型仅在编译时存在 。 在运行时,关于泛型类型的信息(如你的情况<String> )将被删除,并替换为Object类型(看看类型擦除 )。 这就是为什么在运行时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会抛出异常,并停止进一步潜在的危险代码。

你现在可以问“为什么我们甚至不能更早地停止?为什么涉及这样的转换的代码甚至是可编译的?编译器不应该停止它吗?”。 答案是:否,因为编译器无法确定引用所持有的实例的实际类型是什么,并且有可能存在将支持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旁边的Object[]数组可能会存储任何对象 (例如Integers ),所以有可能一个美好的一天我们最终会试图调用方法strArray[i].substring(1,3)没有这种方法的Integer

所以要确保这种情况永远不会发生,Java数组引用只能保持

  • 与参考相同类型的数组的实例(引用String[] strArr可以包含String[]
  • 子类型数组的实例( Object[]可以保存String[]因为StringObject子类型),

但不能保持

  • 来自reference( 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();

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

这个问题的最佳解决方案是通过将这个类型作为方法参数传递来明确地告诉返回什么类型的数组toArray()

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

要么

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

发生的事情是, stock_list.toArray()创建一个Object[]而不是String[] ,因此该类型转换失败1

正确的代码是:

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

甚至

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

(令人惊讶的是,最近的Java版本后者版本更快:请参阅https://.com/a/4042464/139985 )

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

(从技术角度来看,此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???

这显然是无意义的,这就是为什么数组类型通常不兼容分配的原因。


尝试这个

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




arraylist