Stream.sorted()。limit()的性能

时间:2015-07-22 22:20:33

标签: java sorting java-8 time-complexity java-stream

Java Streams同时运行sortedlimit方法,这些方法分别返回流的排序版本并返回仅返回指定数量的流项的流。当这些操作连续应用时,例如:

stream.sorted().limit(qty).collect(Collectors.toList())

是以对qty项进行排序还是对整个列表进行排序的方式执行排序?换句话说,如果qty已修复,此操作是否在O(n)?文档没有单独指定这些方法的性能,也没有相互结合使用。

我问的原因是这些操作的明显必要实现是排序然后限制,花费时间Θ(n * log(n))。但是这些操作可以在O(n * log(qty))中一起执行,智能流式框架可以在执行之前查看整个流以优化这种特殊情况。

3 个答案:

答案 0 :(得分:7)

首先让我首先指出Java语言规范对如何实现流的限制很少。因此,询问Java流的性能真的没有太大意义:它们在实现之间会有很大差异。

另请注意,Stream是一个界面。您可以创建自己的类来实现Stream,以便在sorted上获得所需的任何性能或特殊行为。因此,即使在一个实现的上下文中,真正询问Stream的性能也没有意义。 OpenJDK实现有许多实现Stream接口的类。

话虽如此,如果我们看看OpenJDK实现,流的排序最终会在SortedOps类中(参见source here),你会发现排序方法最终会返回有状态操作的扩展。例如:

private static final class OfInt extends IntPipeline.StatefulOp<Integer>

这些方法检查上游是否已经排序,在哪种情况下它们只是将它传递给下游。它们对于大小的流(即上游)也有特殊的例外情况,它们预先分配它们最终排序的数组,这将提高效率(超过它们用于未知大小流的SpinedBuffer)。但是,只要上游尚未排序,它们就会接受所有项目,然后对它们进行排序,然后发送到下游实例的accept方法。

因此得出的结论是,OpenJDK sorted实现收集所有项目,然后进行排序,然后向下游发送。在某些情况下,当下游将丢弃某些元素时,这将浪费资源。对于特殊情况,您可以自由地实现自己的专用排序操作,该操作比此更有效。可能最直接的方法是实现一个Collector,它保存流中n个最大或最小项的列表。您的操作可能看起来像:

.collect(new CollectNthLargest(4)).stream()

替换

.sorted().limit(4)

答案 1 :(得分:4)

我的StreamEx库中有一个特殊的收藏家执行此操作:MoreCollectors.least(qty)

List<?> result = stream.collect(MoreCollectors.least(qty));

它内部的uses PriorityQueue,在未排序的输入上使用小数量时实际上工作得更快。但是请注意,如果输入主要是排序的,那么sorted().limit(qty)可能会更快,因为TimSort对于预分类数据的速度非常快。

答案 2 :(得分:3)

这取决于实现,也可能取决于流管道是否可以“看到”sorted()limit()之间的潜在操作。

即使您要询问OpenJDK实现,它也可能会发生变化,因为javadocs不保证运行时行为。但不,目前它没有实现k-min选择算法。

您还必须记住sorted()不适用于无限流,除非它们已具有SORTED特征。