哪些操作保留订单

TL; DR; 我正在寻找一个可以查找某个中间操作或终端操作的地方。 我在哪里可以找到这样的文件?

编辑这与如何确保java8流中的处理顺序不重复? ,因为该问题没有提供全面的操作清单。

背景

包文档说:

流是否具有遭遇顺序取决于源和中间操作

在这个优秀的stackoverflow答案中重复了这一点

为了确保在整个流操作中维护订购,您必须研究流的源,所有中间操作和终端操作的文档,以确定它们是否维护订单(或者源是否在第一个订单中有订单)地点)。

这一切都很好,但我应该查看哪些文档? 包文档在示例中提到map保证了排序,但它没有详尽的列表。 Stream类的javadoc记录了一些中间操作,但不是全部。 以map为例:

返回一个流,该流包含将给定函数应用于此流的元素的结果。

这是一个中间操作。

filter

返回由与此给定谓词匹配的此流的元素组成的流。

这是一个中间操作。

这些都没有描述它们是否保留了排序。

此stackoverflow回答声称:

实际上,每个中间操作都默认保留订单。 唯一的例外是:

  • unordered(),它删除了排序约束。
  • sorted()改变了顺序。

如果没有明确指定,您可以假设操作保持订单。 即使distinct()保持顺序,但它为并行流增加了很多复杂性。

但是有任何官方文件可以支持吗?

额外的功劳😉

实际上有两个单独的排序问题。

  1. 操作的输出是否与输入保持相同的顺序?
  2. 操作是否按顺序在每个元素上执行。

例如,并行map操作可以以任意顺序遍历所有元素(违反2.),但仍然保持返回流中的顺序(服从1.)

在对源代码进行一些研究之后,我总结了以下表格:

摘自: Java流 – 第6部分 – Spliterator

下表显示了允许哪些操作类型修改字符:

 | | DISTICTS | SORTED | ORDERED | SIZED | SHORT_CIRCUIT | | ---------------------- | -------- | ------ | ------- | ----- | --------------| | source stream | Y | Y | Y | Y | N | | intermediate operation | PCI | PCI | PCI | PC | PI | | terminal operation | N | N | PC | N | PI | 
  • Y – 允许有
  • P – 可以保留
  • C – 可能会清除。
  • 我 – 可以注射。
  • N – 无效; 与操作无关。

取自Java流 – 流方法特征表

下表显示了每个中间操作 / 终端操作可以打开和关闭的特征和标志:( SHORT_CIRCUIT仅在StreamOpFlag标志的上下文中相关)

注意:除了具有CI (清除和注入)标志的单元外, P (保留)标志被添加到每个单元。

 | | DISTINCT | SORTED | ORDERED | SIZED | SHORT_CIRCUIT | | ---------------- | ----------| --------| ---------| -------| ---------------| | filter | | | | C | | | forEach | | | C | | | | forEachOrdered | | | | | | | allMatch | | | C | | I | | distinct | I | | | C | | | flatMap | C | C | | C | | | anyMatch | | | C | | I | | collect | | | | | | | unOrdered | | | C | | | | count | C | C | C | C | | | findAny | | | C | | I | | findFirst | | | | | I | | flatMapToXXX | C | C | | C | | | limit | | | | C | I | | map | C | C | | | | | mapToXXX | C | C | | | | | max | | | | | | | min | | | | | | | noneMatch | | | C | | I | | peek | | | | | | | reduce | | | | | | | skip | | | C | I | | | sorted | | I | I | | | | toArray | | | | | | 
  • C – 清除。
  • I – 注射。

这有点像两个重复 – 因为你链接的答案实际上解释了事情。 我不知道mapfilter是否应该明确说明它们的保存顺序; 他们不依赖任何先前的州或任何其他州(这些是无国籍的行动),因此暗示他们保留了我所能看到的秩序。 我反过来看,如果他们没有保留订单 – 应该在文档中明确提到; 如果从操作的名称不明显。 例如,如果生成有序流, Stream.generate对我来说并不明显; 因此,在它的文档中说:

返回无限顺序无序流,其中每个元素由提供的Supplier生成。

另一方面, sortedunordered是非常明显的(IMO)改变秩序,至少当你把它们放进去时 – 你明确地说你不关心订单。 unordered顺便说一下,为了满足这个要求,我们不会随意进行任何随机化,你可以在这里阅读更多内容。

一般有两个订单: 处理订单遭遇订单

您可以将遇到订单视为从左到右的处理(假设您有一个List或一个array )。 因此,如果您有一个不改变顺序的管道 – 从左到右看,元素将被馈送到Collector (或任何其他终端操作)。 不是所有的终端操作都是这样的。 一个明显的区别是forEachforEachOrdered ; 或者Collectors.toSet – 根本不需要保留初始订单。 或者让我们把findAny作为终端操作 – 显然你不关心你想要哪个元素,那么为什么要findAny按照确切的顺序喂养findAny呢?

另一方面, 处理顺序没有明确的顺序 – 显然对于并行处理尤其可见。 因此,即使您的管道是并行的(并且在完全不保证任何订单的情况下处理元素),它们仍将按顺序馈送到终端操作 – 如果该终端操作需要这样的订单。