java - 使用 Java 8 中的供应商用泛型列表填充数组抛出删除类型的 ClassCastEx b/c

标签 java arrays generics lambda

我想使用 Supplier 和 Stream.generate 将通用列表作为元素填充一个数组。

看起来像这样:

    Supplier<List<Object>> supplier = () -> new ArrayList<Object>();
    List<Object>[] test = (List<Object>[]) Stream.generate(supplier).limit(m).toArray();

错误输出为:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.util.List;

现在如何使用 Java 8 提供的技术用泛型类型填充数组?或者这根本不可能(还),我必须以“经典”方式来做?

问候, 克拉斯M

编辑

应@Water 的要求,我使用 stream.collect(使用 Cast 测试数组)和传统的迭代方法对填充数组/列表进行了一些性能测试。

首先使用列表进行性能测试:

private static int m = 100000;

/**
 * Tests which way is faster for LISTS.
 * Results:
 * 1k Elements: about the same time (~5ms)
 * 10k Elements: about the same time (~8ms)
 * 100k Elements: new way about 1.5x as fast (~18ms vs ~27ms)
 * 1M Elements: new way about 2x as fast (~30ms vs ~60ms)
 * NOW THIS IS INTERESTING:
 * 10M Elements: new way about .1x as fast (~5000ms vs ~500ms)
 * (100M OutOfMemory after ~40Sec)
 * @param args
 */

public static void main(String[] args) {

    Supplier<String> supplier = () -> new String();
    long startTime,endTime;

    //The "new" way
    startTime = System.currentTimeMillis();
    List<String> test1 =  Stream.generate(supplier).limit(m ).collect(Collectors.toList());
    endTime = System.currentTimeMillis();
    System.out.println(endTime - startTime);


    //The "old" way
    startTime = System.currentTimeMillis();
    List<String> test2 = new ArrayList();
    Iterator<String> i = Stream.generate(supplier).limit(m).iterator();
    while (i.hasNext()) {
        test2.add(i.next());
    }
    endTime = System.currentTimeMillis();
    System.out.println(endTime - startTime);


}

然后是使用数组的性能测试:

    private static int m = 100000000;

    /**
     * Tests which way is faster for ARRAYS.
     * Results:
     * 1k Elements: old way much faster (~1ms vs ~6ms)
     * 10k Elements: old way much faster (~2ms vs ~7ms)
     * 100k Elements: old way about 2x as fast (~7ms vs ~14ms)
     * 1M Elements: old way a bit faster (~50ms vs ~60ms)
     * 10M Elements: old way a bit faster (~5s vs ~6s)
     * 100M Elements: Aborted after about 5 Minutes of 100% CPU Utilisation on an i7-2600k
     * @param args
     */

    public static void main(String[] args) {

        Supplier<String> supplier = () -> new String();
        long startTime,endTime;

        //The "new" way
        startTime = System.currentTimeMillis();
        String[] test1 =  (String[]) Stream.generate(supplier).limit(m ).collect(Collectors.toList()).toArray(new String[m]);
        endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);


        //The "old" way
        startTime = System.currentTimeMillis();
        String[] test2 = new String[m];
        Iterator<String> it = Stream.generate(supplier).iterator();
        for(int i = 0; i < m; i++){
            test2[i] = it.next();
        }
        endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);


    }

}

如您所见,Water 确实是正确的 - Cast 让它变慢了。 但是对于列表,新方法更快;至少 100k - 1M 元素。 我仍然不知道为什么它在处理 10M 元素时要慢得多,我真的很想听听对此的一些评论。

最佳答案

Stream 生成器仍然生成你想要的对象,唯一的问题是调用 toArray() 会给你一个对象数组,你不能从一个对象数组向下转换为一个子对象数组(因为你有类似于:Object[] { ArrayList, ArrayList }).

这是一个正在发生的事情的例子:

你认为你有这个:

    String[] hi = { "hi" };
    Object[] test = (Object[]) hi; // It's still a String[]
    String[] out = (String[]) test;
    System.out.println(out[0]); // Prints 'hi'

但你实际上有:

    String[] hi = { "hi" };
    Object[] test = new Object[1]; // This is not a String[]
    test[0] = hi[0];
    String[] out = (String[]) test; // Cannot downcast, throws an exception.
    System.out.println(out[0]);

您正在返回上面的直接 block ,这就是您遇到转换错误的原因。

有几种解决方法。如果您想遍历您的列表,您可以轻松地从中创建一个数组。

    Supplier<List<Integer>> supplier = () -> { 
        ArrayList<Integer> a = new ArrayList<Integer>();
        a.add(5);
        a.add(8);
        return a;
    };

    Iterator<List<Integer>> i = Stream.generate(supplier).limit(3).iterator();

    // This shows there are elements you can do stuff with.
    while (i.hasNext()) {
        List<Integer> list = i.next();
        // You could add them to your list here.
        System.out.println(list.size() + " elements, [0] = " + list.get(0));
    }

如果你打算处理这个函数,你可以这样做:

    Supplier<List<Integer>> supplier = () -> { 
        ArrayList<Integer> a = new ArrayList<Integer>();
        a.add(5);
        a.add(8);
        return a;
    };

    Object[] objArr = Stream.generate(supplier).limit(3).toArray();
    for (Object o : objArr) {
        ArrayList<Integer> arrList = (ArrayList<Integer>) o; // This is not safe to do, compiler can't know this is safe.
        System.out.println(arrList.get(0)); 
    }

根据Stream Javadocs如果你想把它变成一个数组,你可以使用另一个 toArray() 方法,但我还没有探索这个函数,所以我不想讨论我不知道的东西。

关于java - 使用 Java 8 中的供应商用泛型列表填充数组抛出删除类型的 ClassCastEx b/c,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30374689/

相关文章:

java - 如何使用 Retrofit 2 在同一个键上发布多个值?

c++ - 检查数组的索引是否为空

java - 使用原始类型作为通用对象

java - 向 Mockito.any() 提供类以使 verify() 调用明确的正确语法是什么?

c# - 创建将值作为 Action<T> 实例的字典的语法是什么?

java - 识别自动记录的 url

java - 另一个 Java 泛型 "incompatible types"编译错误

java - 如何修复 Java VM 缺少的类

c - 多客户端聊天程序: send messages to all Clients

php - 显示每个卧室数量的图标(酒店网站)php