哪些操作保留了订单

时间:2017-08-31 09:59:14

标签: java java-8 java-stream

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

修改这不是How to ensure order of processing in java8 streams?的重复,因为该问题不提供全面的操作列表。

背景

the package documentation说:

  

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

this excellent stackoverflow answer

中重复了这一点
  

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

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

为例
  

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

     

这是一个中间操作。

filter

  

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

     

这是一个中间操作。

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

This stackoverflow answer声称:

  

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

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

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

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

额外信用

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

  1. 操作的输出是否与输入保持相同的顺序?
  2. 是否按顺序对每个元素执行操作。
  3. 例如,并行map操作可以以任意顺序遍历所有元素(违反2.),但仍然保持返回流中的顺序(服从1)。

2 个答案:

答案 0 :(得分:17)

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

取自:Java streams - Part 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 streams - Stream methods characteristics table

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

注意:P(保留)标记会添加到除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 - 注入。

答案 1 :(得分:1)

这听起来像两个重复 - 因为你联系的答案实际上都解释了事情。我无法判断mapfilter是否应该明确说明他们保留了订单;他们不依赖任何之前的状态或任何其他状态(这些是无状态操作),所以暗示他们保留了我所看到的顺序。我反过来看,如果他们没有保留命令 - 应该在文档中明确提到;如果从操作的名称不明显。 例如Stream.generate如果生成有序流,对我来说并不明显;因此,在它的文档中说:

  

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

另一方面,

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

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

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

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