当前位置: 首页 > 知识库问答 >
问题:

Java 8:流与集合的性能

束阳旭
2023-03-14

我是Java 8的新手。我仍然不了解这个API,但是我做了一个小型的非正式基准测试来比较新的Streams API与好的旧集合的性能。

测试包括筛选一个integer列表,并对每个偶数计算平方根,并将其存储在double的结果列表中。

代码如下:

    public static void main(String[] args) {
        //Calculating square root of even numbers from 1 to N       
        int min = 1;
        int max = 1000000;

        List<Integer> sourceList = new ArrayList<>();
        for (int i = min; i < max; i++) {
            sourceList.add(i);
        }

        List<Double> result = new LinkedList<>();


        //Collections approach
        long t0 = System.nanoTime();
        long elapsed = 0;
        for (Integer i : sourceList) {
            if(i % 2 == 0){
                result.add(Math.sqrt(i));
            }
        }
        elapsed = System.nanoTime() - t0;       
        System.out.printf("Collections: Elapsed time:\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));


        //Stream approach
        Stream<Integer> stream = sourceList.stream();       
        t0 = System.nanoTime();
        result = stream.filter(i -> i%2 == 0).map(i -> Math.sqrt(i)).collect(Collectors.toList());
        elapsed = System.nanoTime() - t0;       
        System.out.printf("Streams: Elapsed time:\t\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));


        //Parallel stream approach
        stream = sourceList.stream().parallel();        
        t0 = System.nanoTime();
        result = stream.filter(i -> i%2 == 0).map(i -> Math.sqrt(i)).collect(Collectors.toList());
        elapsed = System.nanoTime() - t0;       
        System.out.printf("Parallel streams: Elapsed time:\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));      
    }.
    Collections: Elapsed time:        94338247 ns   (0,094338 seconds)
    Streams: Elapsed time:           201112924 ns   (0,201113 seconds)
    Parallel streams: Elapsed time:  357243629 ns   (0,357244 seconds)

问题:

  • 这个测试公平吗?我犯错了吗?
  • 流比集合慢吗?有没有人在这方面做了一个很好的正式基准?
  • 我应该争取哪种方法?

更新结果。

按照@pveentjer的建议,我在JVM预热(1k次迭代)之后运行了1k次测试:

    Collections: Average time:      206884437,000000 ns     (0,206884 seconds)
    Streams: Average time:           98366725,000000 ns     (0,098367 seconds)
    Parallel streams: Average time: 167703705,000000 ns     (0,167704 seconds)

在这种情况下,流的性能更好。我想知道在一个运行时只调用一两次过滤功能的应用程序中会观察到什么。

共有1个答案

薛博赡
2023-03-14

>

  • 停止使用LinkedList进行任何操作,但使用迭代器从列表中间进行大量删除。

    停止手工编写基准测试代码,使用JMH。

    适当的基准:

    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    @BenchmarkMode(Mode.AverageTime)
    @OperationsPerInvocation(StreamVsVanilla.N)
    public class StreamVsVanilla {
        public static final int N = 10000;
    
        static List<Integer> sourceList = new ArrayList<>();
        static {
            for (int i = 0; i < N; i++) {
                sourceList.add(i);
            }
        }
    
        @Benchmark
        public List<Double> vanilla() {
            List<Double> result = new ArrayList<>(sourceList.size() / 2 + 1);
            for (Integer i : sourceList) {
                if (i % 2 == 0){
                    result.add(Math.sqrt(i));
                }
            }
            return result;
        }
    
        @Benchmark
        public List<Double> stream() {
            return sourceList.stream()
                    .filter(i -> i % 2 == 0)
                    .map(Math::sqrt)
                    .collect(Collectors.toCollection(
                        () -> new ArrayList<>(sourceList.size() / 2 + 1)));
        }
    }
    
    Benchmark                   Mode   Samples         Mean   Mean error    Units
    StreamVsVanilla.stream      avgt        10       17.588        0.230    ns/op
    StreamVsVanilla.vanilla     avgt        10       10.796        0.063    ns/op
    

  •  类似资料:
    • 我试图理解新的Java8流,并且花了好几天的时间在Java8流中的集合上传输嵌套的foreach循环。 是否可以重构以下嵌套的foreach循环,包括Java-8-Streams中的if条件? 如果是,它会是什么样子。 非常感谢你的帮助。 溴

    • 我有一个对象的集合,每个对象都有一个元素的集合,例如: 如何使用Java 8函数式编程来过滤和收集类型等于“a”的集合? 我尝试了以下方法: 但我得到以下错误: 变量'filteredChildren'初始值设定项'parents。stream()。forEach(p- 如何按类型筛选嵌套集合并收集它们?

    • 产出: 请提供2个方法将产生不同输出的例子。

    • 我有一张班友名单 我想分组名单的foo根据他们的位置和总和,我如何实现它与Java流? 我所取得的成就: 我的代码:

    • 我的列表包含像等大小相同的集合。我试过这样做,但似乎不起作用。 我想要的最终结果是。 我可以尝试在中添加所有元素,并对其进行排序,然后为创建一个新的

    • 我知道根级firebase集合是最灵活和可伸缩的,子集合不能轻易删除。在计费方面,我知道firestore的存储数据总量、数据库操作和结果集返回的数据量。但比方说,对于一个包含消息的应用程序,我正在讨论不同的数据库体系结构方法 仅使用Root级别集合: 会话集合字段 成员(用户引用数组) 上次接收时间 会话ID(会话文档参考) 对对话和消息子集合使用根集合 会话集合字段 成员(用户引用数组) la