Преобразование общего списка в массив
Я искал это, но, к сожалению, я не получил правильного ответа.
class Helper {
public static <T> T[] toArray(List<T> list) {
T[] array = (T[]) new Object[list.size()];
for (int i = 0; i < list.size(); i++) {
array[i] = list.get(i);
}
return array;
}
}
Проверьте это:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc");
String[] array = toArray(list);
System.out.println(array);
}
Но есть ошибка:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at test.Helper.main(Helper.java:30)
Как это решить?
UPDATE
Мне нужен этот метод, потому что иногда тип в моем коде слишком длинный:
newEntries.toArray(new IClasspathEntry[0])
Надеюсь позвонить:
toArray(newEntries)
НАКОНЕЦ
Кажется невозможным создать такой метод, спасибо вам большое!
Ответы
Ответ 1
Вы можете просто вызвать list.toArray(T[] array)
и не беспокоиться о его реализации самостоятельно, но, как сказал aioobe, вы не можете создать массив родового типа из-за стирания типа. Если вам нужен этот тип, вам нужно создать типизированный экземпляр самостоятельно и передать его.
Ответ 2
Это связано с стиранием типа. Дженерики удаляются при компиляции, поэтому Helper.toArray
будет скомпилирован для возврата Object[]
.
В этом конкретном случае я предлагаю вам использовать List.toArray(T[])
.
String[] array = list.toArray(new String[list.size()]);
Ответ 3
Если вы хотите создать свой метод с помощью грубой силы, и вы можете гарантировать, что вы вызовете метод только с определенными ограничениями, вы можете использовать отражение:
public static <T> T[] toArray(List<T> list) {
T[] toR = (T[]) java.lang.reflect.Array.newInstance(list.get(0)
.getClass(), list.size());
for (int i = 0; i < list.size(); i++) {
toR[i] = list.get(i);
}
return toR;
}
Этот подход имеет проблемы. Поскольку список может хранить подтипы T, обрабатывая первый элемент списка, поскольку типичный тип создает исключение кастинга, если ваш первый элемент является подтипом. Это означает, что T не может быть интерфейсом. Кроме того, если ваш список пуст, вы получите исключение индекса за пределами.
Это следует использовать, только если вы планируете вызывать метод, в котором первый элемент списка соответствует типу Generic этого списка. Использование предоставленного метода toArray гораздо более надежное, поскольку предоставленный аргумент указывает, какой тип массива вы хотите вернуть.
Ответ 4
Вы не можете создать экземпляр типа Generic, как вы это делали:
T[] array = (T[]) new Object[list.size()];
Как, если T
ограничено типом, вы приписываете новый массив Object
ограниченному типу T
. Я бы предложил использовать List.toArray(T[])
.
Ответ 5
public static <T> T[] toArray(Collection<T> c, T[] a) {
return c.size()>a.length ?
c.toArray((T[])Array.newInstance(a.getClass().getComponentType(), c.size())) :
c.toArray(a);
}
/** The collection CAN be empty */
public static <T> T[] toArray(Collection<T> c, Class klass) {
return toArray(c, (T[])Array.newInstance(klass, c.size()));
}
/** The collection CANNOT be empty! */
public static <T> T[] toArray(Collection<T> c) {
return toArray(c, c.iterator().next().getClass());
}
Ответ 6
String[] array = list.toArray(new String[0]);
Ответ 7
Как указано выше, это будет работать:
String[] array = list.toArray(new String[0]);
И это также будет работать:
String[] array = list.toArray(new String[list.size()]);
Однако в первом случае будет создан новый массив. Вы можете видеть, как это реализовано в Android:
@Override public <T> T[] toArray(T[] contents) {
int s = size;
if (contents.length < s) {
@SuppressWarnings("unchecked") T[] newArray
= (T[]) Array.newInstance(contents.getClass().getComponentType(), s);
contents = newArray;
}
System.arraycopy(this.array, 0, contents, 0, s);
if (contents.length > s) {
contents[s] = null;
}
return contents;
}
Ответ 8
Проблема - это тип компонента массива, который не является строкой.
Кроме того, было бы лучше не предоставлять пустой массив, такой как новый IClasspathEntry [0].
Я думаю, что лучше дать массив с правильной длиной (в противном случае новый будет создан списком # toArray, который является пустой тратой производительности).
Из-за стирания типа решение должно дать тип компонента массива.
Пример:
public static <C, T extends C> C[] toArray(Class<C> componentType, List<T> list) {
@SuppressWarnings("unchecked")
C[] array = (C[])Array.newInstance(componentType, list.size());
return list.toArray(array);
}
Тип C в этой реализации состоит в том, чтобы разрешить создание массива с типом компонента, который является супертипом типов элементов списка.
Использование:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc");
// String[] array = list.toArray(new String[list.size()]); // Usual version
String[] array = toArray(String.class, list); // Short version
System.out.println(array);
CharSequence[] seqArray = toArray(CharSequence.class, list);
System.out.println(seqArray);
Integer[] seqArray = toArray(Integer.class, list); // DO NOT COMPILE, NICE !
}
Ожидание повторных дженериков..
Ответ 9
См. Guava Iterables.toArray(list, class)
.
Пример:
@Test
public void arrayTest() {
List<String> source = Arrays.asList("foo", "bar");
String[] target = Iterables.toArray(source, String.class);
}
Ответ 10
Готовое решение!
Просто скопируйте интерфейс и класс внутри своего проекта.
Это:
public interface LayerDataTransformer<F, T> {
T transform(F from);
Collection<T> transform(Collection<F> from);
T[] toArray(Collection<F> from);
}
и это:
public abstract class BaseDataLayerTransformer<F, T> implements LayerDataTransformer<F, T> {
@Override
public List<T> transform(Collection<F> from) {
List<T> transformed = new ArrayList<>(from.size());
for (F fromObject : from) {
transformed.add(transform(fromObject));
}
return transformed;
}
@Override
public T[] toArray(Collection<F> from) {
Class<T> clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
T[] transformedArray = (T[]) java.lang.reflect.Array.newInstance(clazz, from.size());
int index = 0;
for (F fromObject : from) {
transformedArray[index] = transform(fromObject);
index++;
}
return transformedArray;
}
}
Использование.
Объявить подкласс BaseDataLayerTransformer
public class FileToStringTransformer extends BaseDataLayerTransformer<File,String> {
@Override
public String transform(File file) {
return file.getAbsolutePath();
}
}
И используйте:
FileToStringTransformer transformer = new FileToStringTransformer();
List<File> files = getFilesStub();// returns List<File>
//profit!
String[] filePathArray = transformer.toArray(files);
Ответ 11
Я использую эту просто функцию.
IntelliJ просто ненавидит, что тип типа T [], но он отлично работает.
public static <T> T[] fromCollection(Class<T> c, Collection<T> collection) {
return collection.toArray((T[])java.lang.reflect.Array.newInstance(c, collection.size()));
}
И вызов выглядит следующим образом:
Collection<Integer> col = new ArrayList(Arrays.asList(1,2,3,4));
fromCollection(Integer.class, col);