Преобразовать ArrayList в String [] массив [дубликат]

этот вопрос уже есть ответ здесь:

Я работаю в среде android и попробовал следующий код, но он, похоже, не работает.

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

Если я определяю следующим образом:

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

это работает. Я что-то упускаю?

6 ответов


использовать такой.

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

попробуй такое

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

то, что происходит, это stock_list.toArray() создает Object[], а не String[] и, следовательно, typecast терпит неудачу1.

правильный код будет такой:

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

или даже

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

для получения дополнительной информации обратитесь к javadocs для двух перегрузок List.toArray.

последняя версия использует массив нулевой длины для определения типа массива результатов. (Удивительно, но это быстрее сделать, чем выделить ... по крайней мере, для последних версий Java. См.https://stackoverflow.com/a/4042464/139985 для деталей.)

С технической точки зрения, причина этого поведения / дизайна API заключается в том, что реализация List<T>.toArray() метод не имеет никакой информации о том, что <T> во время выполнения. Все, что он знает, это то, что тип необработанного элемента Object. Напротив, в другом случае параметр array дает базовый тип массива. (Если поставляемый массив достаточно большой, чтобы удерживать элементы списка, он используется. В противном случае в результате выделяется и возвращается новый массив того же типа и большего размера.)


1-на Java, an Object[] не является присвоением, совместимым с String[]. Если бы это было так, то вы могли бы сделать следующее:

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

это явно нонсенс,и именно поэтому типы массивов обычно не совместимы с назначением.


альтернатива в Java 8:

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

я вижу много ответов, показывающих, как решить проблему, но только Стивен пытается объяснить, почему возникает проблема, поэтому я попытаюсь добавить что-то еще по этому вопросу. Это история о возможных причинах, почему Object[] toArray не было изменено на T[] toArray где дженерики ware введены в Java.


почему String[] stockArr = (String[]) stock_list.toArray(); не будет работать?

В Java, универсальный тип существует только во время компиляции. На информация о времени выполнения универсального типа (например, в вашем случае <String>) удаляется и заменяется Object type (взгляните на стирания типа). Вот почему во время выполнения toArray() понятия не имею, какой точный тип использовать для создания нового массива, поэтому он использует Object как самый безопасный тип, потому что каждый класс расширяет объект, чтобы он мог безопасно хранить экземпляр любого класса.

теперь проблема в том, что вы не можете разыграть экземпляр Object[] to String[].

почему? Взгляните на этот пример (предположим, что class B extends A):

//B extends A
A a = new A();
B b = (B)a;

хотя такой код будет компилироваться, во время выполнения мы увидим кинули ClassCastException потому что экземпляр прошла по ссылке a на самом деле не типа B (или его подтипов). Почему эта проблема (почему это исключение должно быть приведено)? Одна из причин в том, что B может есть новые методы/поля, которые A нет, поэтому вполне возможно, что кто-то попытается использовать эти новые члены через b ссылка даже если экземпляр Hold не имеет (не поддерживает) их. Другими словами, мы можем попытаться использовать данные, которых не существует, что может привести ко многим проблемам. Поэтому для предотвращения такой ситуации 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 подана
  • методы, унаследованные от объекта supertype,

так не интерфейс массивов, который делает это невозможным. Проблема в том, что Object[] массив возле Strings может хранить любые объекты (например,Integers) так что вполне возможно, что в один прекрасный день мы закончим с попытками чтобы вызвать метод, как strArray[i].substring(1,3) на примере Integer, который не имеет такого метода.

так сделать обязательно что эта ситуация будет никогда случается, в Java Array ссылки могут содержать только

  • экземпляры массива того же типа, что и reference (reference 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[] массив может содержать только элементы класса 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.

правильный способ сделать это:

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

Я хотел бы добавить к другим отличным ответам здесь и объяснить, как вы могли бы использовать Javadocs, чтобы ответить на ваш вопрос.

Javadoc для toArray() (без аргументов) is здесь. Как вы можете видеть, этот метод возвращает Object[] и не String[] который представляет собой массив типа времени выполнения вашего списка:

public Object[] toArray()

возвращает массив содержащий все элементы в этой коллекции. Если собрание делает любые гарантии как в каком порядке его элементы возвращаются его iterator, этот метод должен возвратить элементы в том же порядке. Возвращаемый массив будет "безопасный" в том, что никакие ссылки на него не поддерживаются коллекцией. (Другими словами, этот метод должен выделить новый массив, даже если коллекция поддерживается массивом). Таким образом, вызывающий абонент может изменять возвращенный матрица.

прямо под этим методом, однако, является в Javadoc на toArray(T[] a). Как вы можете видеть, этот метод возвращает T[] здесь T тип массива вы передаете. Сначала это похоже на то, что вы ищете, но неясно, почему вы передаете массив (вы добавляете к нему, используете его только для типа и т. д.). Документация дает понять, что цель переданного массива по существу заключается в определении типа массива для возвращение (именно в вашем случае):

public <T> T[] toArray(T[] a)

возвращает массив, содержащий все элементы в этой коллекции; тип среды выполнения возвращаемого массива это из указанного массива. если коллекция соответствует указанному массив, он возвращается туда. В противном случае, выделяется новый массив с типом среды выполнения указанного массива и размером этого коллекция. Если коллекция соответствует указанному массив c помещением для запасной (т. е. массив имеет больше элементов, чем коллекция), элемент в массиве сразу после конца коллекции имеет значение null. Это полезно при определении длины коллекция только в том случае, если вызывающий объект знает, что коллекция не содержат любые нулевые элементы.)

если эта коллекция дает какие-либо гарантии относительно того, какой порядок ее элементов возвращаются итератором, этот метод должен возвращать элементы в тот же порядок.

эта реализация проверяет, достаточно ли большой массив, чтобы содержать коллекции; если нет, он выделяет новый массив нужного размера и тип (с использованием отражения). Затем выполняется итерация по коллекции, сохранение каждой ссылки на объект в следующем последовательном элементе массив Array, начиная с элемента 0. Если массив больше, чем коллекция, null хранится в первом расположении после окончания коллекция.

Of конечно, понимание дженерики (как описано в других ответах) требуется, чтобы понять разницу между этими двумя методами. Тем не менее, если вы сначала пойдете в Javadocs, вы обычно найдете свой ответ, а затем увидите сами, что еще вам нужно узнать (если вы действительно это делаете).

также обратите внимание, что чтение Javadocs здесь помогает вам понять, какая структура массива, который вы передаете, должна быть. Хотя на самом деле это может и не иметь значения. не должно проходить в пустом массиве, как это:

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

потому что из doc эта реализация проверяет, достаточно ли большой массив, чтобы содержать коллекцию;если нет, он выделяет новый массив правильного размера и типа (используя отражение). нет необходимости в дополнительных накладных расходах при создании нового массива, когда вы можете легко передать размер.

как это обычно бывает, Javadocs предоставить вам богатство информации и направления.

Эй, подождите минутку, что такое отражение?