Java .forEach(list::add) 和 .collect(Collectors.toList()) 的区别

3

什么更快?

List<E> bar = new ArrayList<>();
pan.stream() /* other functions */.forEach(bar::add);

或者

List<E> bar = pan.stream() /* other functions */.collect(Collectors.toList());

11
你为什么问我们而不是进行分析? - Joseph Sible-Reinstate Monica
你的其他函数是否包含过滤功能?如果没有,你可以使用Collectors.toCollection,并显式指定初始大小(以避免调整后备数组的大小)。 - Jacob G.
4
清晰明了几乎总是比微小的优化更为重要。后者可能更实用,也更有可能是最佳选择。 - Peter Lawrey
2
这就是Java 8的“声明式”编程范式的美妙之处:你只需要告诉Java需要做什么,比如将数据收集到一个集合中,然后让Java自己去考虑如何实现。你的第二个例子就是一个更清晰的示例。 - Hovercraft Full Of Eels
1个回答

3
我已经对一个包含一百万项的列表进行了两种情况的测试。总体而言,顺序流程几乎没有差别,但并行流程有所不同。
Benchmark                    Mode  Cnt  Score    Error   Units
Performance.collect          avgt  200  0.022 ±  0.001   s/op
Performance.forEach          avgt  200  0.021 ±  0.001   s/op
Performance.collectParallel  avgt  200  0.124 ±  0.004   s/op
Performance.forEachParallel  avgt  200  0.131 ±  0.001   s/op

我的看法是,你不应该使用forEach来创建列表,因为它会违反函数纯度规则,而且在使用并行流时,使用collect更加高效。

@Benchmark @BenchmarkMode(Mode.AverageTime)
public void collect(Blackhole blackhole) {
    Stream<Double> stream = Stream.iterate(0.0, e -> Math.random());
    List<Double> list = stream.limit(1000000).collect(Collectors.toList());
    blackhole.consume(list);
}

@Benchmark @BenchmarkMode(Mode.AverageTime)
public void forEach(Blackhole blackhole) {
    Stream<Double> stream1 = Stream.iterate(0.0, e -> Math.random());
    List<Double> list = new ArrayList<>();
    stream1.limit(1000000).forEach(e -> list.add(e));
    blackhole.consume(list);
}

@Benchmark @BenchmarkMode(Mode.AverageTime)
public void collectParallel(Blackhole blackhole) {
    Stream<Double> stream = Stream.iterate(0.0, e -> Math.random());
    List<Double> list = stream.parallel().limit(1000000).collect(Collectors.toList());
    blackhole.consume(list);
}

@Benchmark @BenchmarkMode(Mode.AverageTime)
public void forEachParallel(Blackhole blackhole) {
    Stream<Double> stream1 = Stream.iterate(0.0, e -> Math.random());
    List<Double> list = Collections.synchronizedList(new ArrayList<>());
    stream1.parallel().limit(1000000).forEach(e -> list.add(e));
    blackhole.consume(list);
}

2
这不是一个准确的基准测试。我建议使用现有的框架,比如JMH。 - Jacob G.
1
另外,你有检查过这个解决方案的准确性吗?我曾经发现过在使用并行流的情况下,通过forEach(list::add)方法将null添加到ArrayList中。 - Joe C
区别一点儿也不大。以不同的顺序运行代码,你可能会感到惊讶。 - Jacob G.
5
多好的测试!在单个JVM中,使用单个方法比较了1000万个流和100万个流,并测量生成随机数并执行大量垃圾回收的OSR存根 - apangin
1
我在流的大小上犯了一个可怕的错误,已经更新了答案。 - Michael Dz
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接