2017-08-31 30 views
11

TL; DR;我正在寻找一个地方,我可以查找某个中间或终端操作。我在哪里可以找到这样的文档?Java 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. 操作是否按顺序在每个元素上执行。

例如,并行map操作上可以继续以任意顺序的所有元素(违反2),但仍维持返回流中的顺序(1服从)

+0

另请参见https://stackoverflow.com/a/29218074/32453 – rogerdpack

回答

7

后一些源代码研究我summerized如下表:

来自Streams In Depth - Chapter 7: Spliterator

哪些操作类型允许修改charactersiticts下表showes:

|      | 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 - 可能会清除。
  • I - 可以注射。
  • N - 无效;非常适合手术。

Streams In Depth - Stream methods characteristics table

下表showes每个中间操作/终端操作 5接通和断开,其特征和标志摘自:(SHORT_CIRCUIT仅在StreamOpFlag上下文中相应和标志)

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

|     | 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 - 注射。
+0

我修复了其中一些标志;首先,'forEach'和'forEachOrdered'之​​间的根本区别在于'forEach'不考虑每个合同的顺序,即使当前实现可能不会在内部清除该标志;这种设置/清除逻辑不适合终端操作。此外,没有理由说明为什么元素的顺序应该与'count'操作相关,顺便说一句,在Java 9中可能会短路。并且说'sort'同时清除并注入'SORTED'没有多大意义,尤其是,因为它是稳定的... – Holger

+0

此表仅反映**实现的**当前状态。 **为什么**他们是否在码头操作中注射/清除旗帜是一个不容忽视的问题,应该进行调查。感谢您的注意和编辑。 –

+1

好吧,有一种* unsorted *终端操作的反向传播到流管道,它可能影响操作,但是它更复杂, 'Collector'可以是['UNORDERED'](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.Characteristics.html#UNORDERED),因此,一个'collect操作可能会将* unordered *状态传播到流管道。对于其他特征,终端操作是否可以利用它会更有趣,例如, 'toArray'将受益于'SIZED'特性,'count'将在Java 9中受到... – Holger

1

这有点听起来像两个重复 - 因为你链接的答案实际上解释了事情。我不知道mapfilter应该专门说他们保存顺序;他们不依赖任何先前的状态或任何其他状态(这些是无状态操作),因此它是暗示,他们保持顺序,据我所知。我反过来看,如果他们不保留秩序 - 应该在文档中明确提及;如果从操作的名称不明显。 例如Stream.generate对我来说并不明显,如果它生成一个有序的流;因此这在文档中有说明:

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

sortedunordered,另一方面是相当明显的(IMO)更改订单,至少当你把这些 - 你明确地说,你不关心顺序。 unordered顺便说一句,不会有目的的任何随机,以满足这一点,你可以阅读更多here

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

你可以想想遭遇订单加工由左到右(想象你有Listarray)。所以如果你有一个不改变顺序的流水线 - 元素将会从左到右看到Collector(或任何其他的终端操作)。那么不是所有的终端操作都是这样的。一个明显的区别是forEachforEachOrdered;或者Collectors.toSet - 根本不需要保存初始订单。或者让我们以findAny作为终端操作 - 显然你并不关心你想要哪个元素,那么为什么要按照确切的顺序喂养findAny呢?

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

+0

我会走到目前为止,说*返回*无序*流的每个*方法记录为这样。这也适用于'unordered()',它的文档指出它返回一个无序的流,而'sorted()'确实遵守遇到顺序,因为它将对有序流使用稳定的排序。 – Holger