Java Stream.toList()的性能会比collector.toList()更好吗

Java Stream.toList()的性能会比collector.toList()更好吗,java,performance,java-stream,jmh,java-16,Java,Performance,Java Stream,Jmh,Java 16,JDK正在引入一个API。下面是一个基准代码,我试图将其性能与现有的收集器进行比较。toList: @BenchmarkMode(Mode.All) @Fork(1) @State(Scope.Thread) @Warmup(iterations = 20, time = 1, batchSize = 10000) @Measurement(iterations = 20, time = 1, batchSize = 10000) public class CollectorsVsStreamT

JDK正在引入一个API。下面是一个基准代码,我试图将其性能与现有的
收集器进行比较。toList

@BenchmarkMode(Mode.All)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 20, time = 1, batchSize = 10000)
@Measurement(iterations = 20, time = 1, batchSize = 10000)
public class CollectorsVsStreamToList {

    @Benchmark
    public List<Integer> viaCollectors() {
        return IntStream.range(1, 1000).boxed().collect(Collectors.toList());
    }

    @Benchmark
    public List<Integer> viaStream() {
        return IntStream.range(1, 1000).boxed().toList();
    }
}
当然,领域专家的第一个问题是,基准测试程序是否正确?测试类是在MacOS上执行的。如需更多详细信息,请务必告知我


跟进,据我从读数推断,
流的平均时间、吞吐量和采样时间。toList
看起来比
收集器好。toList
。这种理解正确吗?

Stream::toList
构建在
toArray
之上,而不是
collect
。在
toArray
中有许多优化,使其可能比收集更快,尽管这在很大程度上取决于细节。如果流管道(从源到最终中间操作)的大小为
大小
,则可以预先确定目标数组的大小(而不是像
toList
收集器必须做的那样进行潜在的重新分配)。如果管道进一步被
子化
,则并行执行不仅可以预先确定结果数组的大小,但是可以计算精确的每碎片偏移量,这样每个子任务都可以将其结果精确地放在正确的位置,从而无需将中间结果复制到最终结果中


因此,根据细节,
toList
可能比
collect

快得多。在某些情况下,
Stream::toList
很可能更有效,但这实际上取决于细节
Stream::toList
建立在
toArray
的基础上,对于具有大小(对于并行流,理想情况下是补贴)特征的源,
toArray
经过优化,以减少与
collect
相比的重新分配和复制。但是,如果您试图测量两个收集器之间的差异,您应该尽量减少流管线其余部分的工作量。我会将源代码生成和装箱从基准方法中移出,进入一个在基准方法之外初始化的
@状态
变量,类似于
Stream.of(data).toList()
。拳击确实在扭曲你的数据。我还将包括并行运行。@Holger默认实现中的额外步骤是在
发生这种情况时强制进行防御复制。toArray
违反了其规范并保留了对返回数组的引用。如果没有防御性副本,则可以修改从默认的
toList
实现返回的列表。@StuartMarks
toArray
是流API的一部分,因此如果实现违反规范并返回共享数组,
toArray
方法的调用方可能已经中断。为什么
toList()
方法的调用方比
toArray()的调用方获得更多的保证?这很简单:如果实现尊重规范,那么这些方法按照规范所说的去做。修复一个可能损坏的实现不是默认实现的任务。防御副本适用于基本类,如
String
,但不适用于包装器,因为包装器无论如何都不能保证是真正不可变的集合。@StuartMarks好的,这不是合适的地方。最后一点意见是,在很多地方,JDK代码的结果取决于未知接口实现的正确性,而在这里使用这种不信任/昂贵的防御代码看起来是任意的。而且,在文档中,这显然是不必要的。即使对于未经大小调整的流,内部使用的spined缓冲区也可能比填充
ArrayList
之类的
Collectors.toList()
更有效。
Benchmark                                                       Mode  Cnt   Score    Error  Units
CollectorsVsStreamToList.viaCollectors                         thrpt   20  17.321 ±  0.583  ops/s
CollectorsVsStreamToList.viaStream                             thrpt   20  23.879 ±  1.682  ops/s
CollectorsVsStreamToList.viaCollectors                          avgt   20   0.057 ±  0.002   s/op
CollectorsVsStreamToList.viaStream                              avgt   20   0.040 ±  0.001   s/op
CollectorsVsStreamToList.viaCollectors                        sample  380   0.054 ±  0.001   s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.00    sample        0.051            s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.50    sample        0.054            s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.90    sample        0.058            s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.95    sample        0.058            s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.99    sample        0.062            s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.999   sample        0.068            s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p0.9999  sample        0.068            s/op
CollectorsVsStreamToList.viaCollectors:viaCollectors·p1.00    sample        0.068            s/op
CollectorsVsStreamToList.viaStream                            sample  525   0.039 ±  0.001   s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.00            sample        0.037            s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.50            sample        0.038            s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.90            sample        0.040            s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.95            sample        0.042            s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.99            sample        0.050            s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.999           sample        0.051            s/op
CollectorsVsStreamToList.viaStream:viaStream·p0.9999          sample        0.051            s/op
CollectorsVsStreamToList.viaStream:viaStream·p1.00            sample        0.051            s/op
CollectorsVsStreamToList.viaCollectors                            ss   20   0.060 ±  0.007   s/op
CollectorsVsStreamToList.viaStream                                ss   20   0.043 ±  0.006   s/op