首页 文章

将ArrayList <String>转换为String []数组[duplicate]

提问于
浏览
980

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

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

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

如果我定义如下:

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

有用 . 有什么东西我不见了吗?

6 回答

  • 83

    正确的方法是:

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

    我想在这里添加其他很好的答案并解释你如何使用Javadocs来回答你的问题 .

    toArray() (无参数)的Javadoc是here . 如您所见,此方法返回 Object[] 而不是 String[] ,它是列表的运行时类型的数组:

    public Object [] toArray()返回包含此collection中所有元素的数组 . 如果集合对其迭代器返回的元素的顺序做出任何保证,则此方法必须以相同的顺序返回元素 . 返回的数组将是“安全的”,因为集合不会保留对它的引用 . (换句话说,即使集合由数组支持,此方法也必须分配新数组) . 因此调用者可以自由修改返回的数组 .

    但是,在该方法的正下方,the JavadoctoArray(T[] a) . 正如你所看到的,这个方法返回一个 T[] ,其中 T 是你传入的数组的类型 . 首先,这似乎是你不清楚为什么你传入一个数组(你是否添加它,使用它)仅适用于类型等) . 该文档清楚地表明,传递数组的目的主要是定义要返回的数组类型(这正是您的用例):

    public <T> T [] toArray(T [] a)返回包含此集合中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型 . 如果集合适合指定的数组,则返回其中 . 否则,将使用指定数组的运行时类型和此集合的大小分配新数组 . 如果集合适合指定的数组,并且有空余空间(即,数组的元素多于集合),则紧跟集合结尾的数组中的元素将设置为null . 仅当调用者知道集合不包含任何null元素时,这在确定集合的长度时很有用 . )如果此集合对其迭代器返回其元素的顺序做出任何保证,则此方法必须返回元素以相同的顺序 . 此实现检查数组是否足够大以包含集合;如果没有,它会分配一个正确大小和类型的新数组(使用反射) . 然后,它遍历集合,将每个对象引用存储在数组的下一个连续元素中,从元素0开始 . 如果数组大于集合,则在集合结束后的第一个位置存储null .

    当然,需要理解泛型(如其他答案中所述)才能真正理解这两种方法之间的区别 . 然而,如果你第一次去Javadocs,你通常会找到你的答案,然后亲自看看你还需要学习什么(如果你真的这样做) .

    另请注意,在此处阅读Javadoc可帮助您了解传入的数组的结构应该是什么 . 虽然它可能实际上并不重要,但你不应该像这样传入一个空数组:

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

    因为,从doc,此实现检查数组是否足够大以包含集合;如果没有,它会分配一个正确大小和类型的新数组(使用反射) . 当你可以轻松传入大小时,不需要额外的开销来创建一个新数组 .

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

    嘿等一下,有什么反映?

  • 1604

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


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

    在Java中, generic type exists at compile-time only . 在运行时,有关泛型类型的信息(如您的情况 <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抛出异常,并停止进一步的潜在危险代码 .

    你现在可以问"So why aren't we stopped even earlier? Why code involving such casting is even compilable? Shouldn't compiler stop it?" . 答案是:不,因为编译器无法确定 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超类型,

    所以它不是阵列接口使它变得不可能 . 问题是 Object[] array beside Strings can store any objects (例如 Integers )所以有可能在一个美好的日子我们最终会尝试调用像 strArray[i].substring(1,3) 这样的方法,如 Integer 没有这样的方法 .

    因此,为了确保这种情况永远不会发生,Java数组引用只能保留

    • 与引用类型相同的数组实例(引用 String[] strArr 可以容纳 String[]

    • 子类型数组的实例( Object[] 可以保存 String[] ,因为 StringObject 的子类型),

    但不能坚持

    • 来自引用的数组类型的超类型数组( 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 class new 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.
    
  • 277

    发生的事情是 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???
    

    这显然是无稽之谈,这就是为什么数组类型通常不是赋值兼容的原因 .

  • 17

    Java 8中的替代方案:

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

    像这样使用 .

    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);
    
  • 130

    试试这个

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

相关问题