将通用列表转换为数组

时间:2011-06-29 14:24:22

标签: java generics

我已经搜索过这个,但不幸的是,我没有得到正确答案。

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)

如何解决这个问题?


更新

我想要这种方法,因为有时我的代码中的类型太长了:

newEntries.toArray(new IClasspathEntry[0])

我希望打电话:

toArray(newEntries)

最后

创建这样的方法似乎是不可能的,非常感谢你们!

12 个答案:

答案 0 :(得分:43)

这是由于类型擦除。在编译中删除了泛型,因此Helper.toArray将被编译为返回Object[]

对于这种特殊情况,我建议您使用List.toArray(T[])

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

答案 1 :(得分:35)

您可以调用list.toArray(T[] array)而不必担心自己实现它,但正如aioobe所说,由于类型擦除,您无法创建泛型类型的数组。如果您需要返回该类型,则需要自己创建一个类型化实例并将其传入。

答案 2 :(得分:18)

如果你想通过蛮力制作你的方法,并且你可以保证你只会在某些限制下调用该方法,你可以使用反射:

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不能是一个接口。此外,如果您的列表为空,您将获得索引超出范围的异常。

仅当您计划调用列表的第一个元素与列表的通用类型匹配的方法时,才应使用此选项。使用提供的toArray方法更加健壮,因为提供的参数告诉您要返回的数组类型。

答案 3 :(得分:9)

您无法像在此处一样实例化Generic类型:

 T[] array = (T[]) new Object[list.size()];

因为,如果T与某个类型绑定,则会将新的Object数组类型转换为有界类型T。我建议改用List.toArray(T[])方法。

答案 4 :(得分:8)

请参阅Guava的Iterables.toArray(list, class)

示例:

@Test
public void arrayTest() {
    List<String> source = Arrays.asList("foo", "bar");
    String[] target = Iterables.toArray(source, String.class);
}

答案 5 :(得分: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 :(得分:4)

String[] array = list.toArray(new String[0]);

答案 7 :(得分:4)

如前所述,这将起作用:

String[] array = list.toArray(new String[0]);

这也有效:

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

但是,在第一种情况下,将生成一个新数组。你可以看到这是implemented in 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 :(得分:3)

问题是不是String的数组的组件类型。

此外,最好不提供空数组,例如新的IClasspathEntry [0]。 我认为最好给出一个具有正确长度的数组(否则将使用List#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 :(得分:1)

工作解决方案!

只需复制项目中的界面和类。 这个:

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

答案 10 :(得分:1)

我使用这个简单的功能。 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);

答案 11 :(得分:0)

拥有通用enter code here时,您将能够在运行时知道对象的类。因此,实现它的最佳方法是这样的:

List<T>

为什么需要public static <T> T[] list2Array(Class<T[]> clazz, List<T> elements) { T[] array = clazz.cast(Array.newInstance(clazz.getComponentType(), elements.size())); return elements.toArray(array); } 参数?

因为,我们有一个通用的列表,它不会提供必要的信息,以在保持类型安全的同时,获取我们正在寻找的精确类型的数组。与其他答案相反,这将使您返回一个Object数组或在编译时导致警告。这种方法将为您提供一个干净的解决方案。这里的“ hack”是Class<T[]>调用,对于您声明clazz.cast()实例的任何类型,该调用都不会发出警告。

现在,如何使用它?

简单,只需这样命名:

list2Array()

这是此的编译示例:https://ideone.com/wcEPNI

为什么起作用?

之所以起作用,是因为编译器将类文字视为List<String> list = Stream.of("one", "two", "three").collect(Collectors.toList()); String[] numbers = list2Array(String[].class, list); System.out.println(Arrays.toString(numbers)); 的实例。这也适用于接口,枚举,任意维数组(例如java.lang.Class),基元和关键字void。

String[].class本身是通用的(声明为Class,其中Class<T[]>代表T[]对象表示的类型),这意味着{{1的类型}}是Class

注意:由于基本体不能用于类型变量,因此您将无法获得基本体数组。