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

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

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

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

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


答案 1

首先,我要提出一个一般性的观点,即 Java 语言规范对流的实现方式几乎没有限制。因此,询问Java流的性能真的不是太有意义:它在实现之间会有很大差异。

另请注意,这是一个接口。您可以创建自己的类,该类实现为具有所需的任何性能或特殊行为。因此,即使在一个实现的上下文中,真正询问 的性能也是没有意义的。OpenJDK实现有很多实现接口的类。StreamStreamsortedStreamStream

话虽如此,如果我们看一下OpenJDK实现,流排序最终在类中结束(请参阅此处的源代码),您会发现排序方法最终返回有状态操作的扩展。例如:SortedOps

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

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

因此,由此得出的结论是,OpenJDK实现收集所有项目,然后进行排序,然后向下游发送。在某些情况下,这将浪费资源,而下游将丢弃一些元素。您可以自由实施自己的专业分拣操作,该操作在特殊情况下比这更高效。可能最直接的方法是实现一个,该列表保留流中n个最大或最小项目的列表。然后,您的操作可能如下所示:sortedCollector

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

替换

.sorted().limit(4)

答案 2

我的 StreamEx 库中有一个特殊的收集器可以执行以下操作:MoreCollectors.least(qty)

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

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