Преобразование общего списка в массив - программирование
Подтвердить что ты не робот

Преобразование общего списка в массив

Я искал это, но, к сожалению, я не получил правильного ответа.

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)

НАКОНЕЦ

Кажется невозможным создать такой метод, спасибо вам большое!

4b9b3361

Ответ 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);