Преобразовать массив ArrayList <String> в String []
Я работаю в среде Android и пробовал следующий код, но он, похоже, не работает.
String [] stockArr = (String[]) stock_list.toArray();
Если я определяю следующее:
String [] stockArr = {"hello", "world"};
он работает. Есть что-то, что мне не хватает?
Ответы
Ответ 1
Используйте это.
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);
Ответ 2
Попробуйте это
String[] arr = list.toArray(new String[list.size()]);
Ответ 3
Что происходит, так это то, что stock_list.toArray()
создает Object[]
а не String[]
и, следовательно, при ошибке типа 1 не выполняется.
Правильный код:
String [] stockArr = stockList.toArray(new String[stockList.size()]);
или даже
String [] stockArr = stockList.toArray(new String[0]);
(Удивительно, но последняя версия быстрее в последних версиях Java: см. fooobar.com/questions/10844/...)
Для получения дополнительной информации см. Javadocs для двух перегрузок List.toArray
.
(С технической точки зрения, причина этого поведения/дизайна API заключается в том, что реализация метода List<T>.toArray()
не имеет информации о том, что во время выполнения <T>
. Все, что он знает, это то, что исходный элемент type is Object
. В отличие от этого, в другом случае параметр массива дает базовый тип массива. (Если поставляемый массив достаточно велик, он используется. В противном случае новый массив того же типа и большего размера будет выделено и возвращено в результате.)
1 - В Java Object[]
не является присвоением, совместимым со String[]
. Если бы это было так, вы могли бы сделать это:
Object[] objects = new Object[]{new Cat("fluffy")};
Dog[] dogs = (Dog[]) objects;
Dog d = dogs[0]; // Huh???
Это явно бессмыслица, и поэтому типы массивов обычно не совместимы с назначением.
Ответ 4
Альтернатива в Java 8:
String[] strings = list.stream().toArray(String[]::new);
Ответ 5
Я вижу много ответов, показывающих, как решить проблему, но только Ответ Стивена пытается объяснить, почему возникает проблема, поэтому я попытаюсь добавить что-то еще по этому вопросу. Это рассказ о возможных причинах, по которым Object[] toArray
не был изменен на T[] toArray
, где в Java появились файлы generics.
Почему 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;
Хотя такой код будет скомпилирован, во время выполнения мы увидим throw ClassCastException
, потому что экземпляр, содержащий ссылку a
, на самом деле не имеет тип B
(или его подтипы). Почему эта проблема (почему это исключение нужно бросить)? Одна из причин заключается в том, что B
может иметь новые методы/поля, которые a
не имеет, поэтому возможно, что кто-то попытается использовать эти новые элементы с помощью ссылки B
, даже если у удерживаемого экземпляра нет ( не поддерживает) их. Другими словами, мы могли бы попытаться использовать данные, которых не существует, что может привести к множеству проблем. Поэтому, чтобы предотвратить такую ситуацию, 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
,
- унаследованные от супертипа объекта,
Так что это не интерфейс массивов, который делает невозможным. Проблема заключается в том, что массив Object[]
рядом с Strings
может хранить любые объекты (например, Integers
), поэтому возможно, что в один прекрасный день мы закончим попыткой вызвать метод типа strArray[i].substring(1,3)
например, Integer
, который не имеет такого метода.
Итак, чтобы убедиться, что этой ситуации никогда не будет, ссылки на Java могут содержать только
- экземпляры массива того же типа, что и ссылка (ссылка
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
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.
Ответ 6
Правильный способ сделать это:
String[] stockArr = stock_list.toArray(new String[stock_list.size()]);
Я хотел бы добавить к другим замечательным ответам здесь и объяснить, как вы могли использовать Javadocs для ответа на ваш вопрос.
Javadoc для toArray()
(без аргументов) здесь. Как вы можете видеть, этот метод возвращает Object[]
, а не String[]
, который является массивом типа времени выполнения вашего списка:
public Object[] toArray()
Возвращает массив, содержащий все элементов в этой коллекции. Если коллекция предоставляет какие-либо гарантии как в какой порядок его элементы возвращаются его итератором, этот метод должны возвращать элементы в том же порядке. Возвращенный массив будет "безопасный", поскольку ссылки на него не поддерживаются коллекцией. (Другими словами, этот метод должен выделять новый массив, даже если коллекция поддерживается массивом). Таким образом, вызывающий абонент может изменять возвращенный массив.
Прямо ниже этого метода, однако, Javadoc для toArray(T[] a)
. Как вы можете видеть, этот метод возвращает T[]
, где T
- тип массива, в который вы проходите. Сначала это похоже на то, что вы ищете, но неясно, почему именно вы передаете массив (вы добавляете к нему, используя его только для типа и т.д.). Документация дает понять, что цель переданного массива состоит в том, чтобы определить тип возвращаемого массива (который является именно вашим прецедентом):
public <T> T[] toArray(T[] a)
Возвращает массив, содержащий все элементы в этой коллекции; тип времени выполнения возвращаемого массива того из указанного массива. Если коллекция соответствует указанному массив, он возвращается в нем. В противном случае выделяется новый массив с типом времени выполнения указанного массива и размером этого коллекция. Если коллекция подходит в указанном массиве с комнатой для запасной (т.е. массив имеет больше элементов, чем сбор), элемент в массиве сразу после окончания коллекции имеет значение null. Это полезно при определении длины сбор, только если вызывающий абонент знает, что коллекция не содержат любые нулевые элементы.)
Если этот сборник дает какие-либо гарантии относительно того, в каком порядке его элементы возвращаются его итератором, этот метод должен возвращать элементы в тот же порядок.
Эта реализация проверяет, достаточно ли массив достаточно, чтобы содержать коллекция; если нет, он выделяет новый массив правильного размера и тип (с использованием отражения). Затем он выполняет итерацию по коллекции, сохраняя каждую ссылку на объект в следующем последовательном элементе array, начиная с элемента 0. Если массив больше, чем коллекции, нуль хранится в первом месте после окончания коллекции.
Конечно, понимание дженериков (как описано в других ответах) необходимо для понимания разницы между этими двумя методами. Тем не менее, если вы впервые отправитесь в Javadocs, вы, как правило, найдете свой ответ, а затем сами убедитесь, что еще нужно изучить (если вы действительно это делаете).
Также обратите внимание, что чтение Javadocs помогает вам понять, какова должна быть структура массива, в который вы проходите. Хотя на самом деле это не имеет особого значения, вы не должны передавать пустой массив следующим образом:
String [] stockArr = stockList.toArray(new String[0]);
Потому что, из документа, эта реализация проверяет, достаточно ли массива, чтобы содержать коллекцию; если нет, он выделяет новый массив правильного размера и типа (используя отражение). Там нет необходимости в дополнительных накладных расходах при создании нового массива, когда вы можете легко передать размер.
Как обычно, Javadocs предоставляют вам массу информации и направлений.
Эй, подожди минутку, какое отражение?