假设我们有一个标准的流操作方法链:
Arrays.asList("a", "bc", "def").stream()
.filter(e -> e.length() != 2)
.map(e -> e.length())
.forEach(e -> System.out.println(e));
JLS中是否有关于流操作应用于列表元素的顺序的保证?
例如,是否保证:
"bc"
之前,不会将过滤谓词应用于"a"
?"def"
之前,不会将映射函数应用于"a"
?1
将在3
之前打印? 注意:我在这里专门谈论stream()
,不 parallelStream()
,其中预计映射和过滤等操作都在平行。
答案 0 :(得分:9)
您想知道的所有内容都可以在java.util.stream
JavaDoc中找到。
订购
流可能有也可能没有定义的遭遇订单。是否 一个流有一个遭遇顺序取决于源和 中间操作。某些流源(例如List或 数组)本质上是有序的,而其他的(如HashSet) 不是。某些中间操作(例如sorted())可能会强制执行 在其他无序流上遇到订单,其他人可能会 渲染无序的有序流,例如BaseStream.unordered()。 此外,一些终端操作可以忽略遭遇顺序,例如 的forEach()。
如果订购了流,则大多数操作都被限制为可以进行操作 遇到顺序中的元素; 如果流的来源是a 列表包含[1,2,3],然后执行map的结果(x - > x * 2) 必须是[2,4,6]。但是,如果源没有定义的遭遇 顺序,那么值[2,4,6]的任何排列都是有效的 结果
对于顺序流,是否存在遭遇订单 不影响表现,只影响决定论。如果订购了流, 在相同的流程上重复执行相同的流管道 源将产生相同的结果;如果没有订购, 重复执行可能会产生不同的结果。
对于并行流,有时可以放宽排序约束 实现更高效的执行。某些聚合操作,例如 过滤重复(distinct())或分组缩减 (Collectors.groupingBy())可以更有效地实现 元素的排序是不相关的。同样,操作也是如此 本质上与遇到订单相关联,例如limit(),可能需要 缓冲以确保正确的订购,从而破坏了利益 并行性。如果流有遭遇订单,但是 用户并不特别关心那次遭遇订单 使用无序()对流进行排序可以改善并行性 某些有状态或终端操作的性能。但是,大多数 流管道,例如块的权重总和"上面的例子, 即使在排序约束下,仍然可以高效并行化。
答案 1 :(得分:4)
JLS中是否有关于流操作应用于列表元素的顺序的保证?
JLS不涵盖Streams库。您需要阅读库的Javadoc。
Streams也支持并行流,处理事物的顺序取决于实现。
将过滤谓词应用于" bc"在将过滤谓词应用于" a"?
之前不会发生
可以合理地假设它会,但你不能保证它,也不应该编写需要这种保证的代码,否则你以后就无法并行化了。
将映射函数应用于" def"在将映射函数应用于" a"?
之前不会发生
安全假设这确实发生了,但你不应该编写需要它的代码。
答案 2 :(得分:1)
无法保证将列表项传递给谓词lambdas的顺序。流文档可以保证流的输出,包括遇到的顺序;它不保证实现细节,例如应用过滤谓词的顺序。
因此,文档不会阻止filter
读取多个元素,以相反的顺序在它们上运行谓词,然后将传递谓词的元素按顺序发送到流的输出中他们进来了。我不知道为什么filter()
会这样做,但这样做不会破坏文件中的任何保证。
您可以从filter()
将按照集合提供的顺序对元素调用谓词的文档进行非常强大的推断,因为您在列表中传递了调用stream()
的结果,调用Collection.stream()
,并根据Java文档,保证以这种方式生成的Stream<T>
顺序:
返回以此集合为源的顺序
Stream
。
此外,filter()
无状态:
无状态操作(例如
filter
和map
)在处理新元素时不保留先前看到的元素的状态 - 每个元素都可以独立于其他元素的操作进行处理。
因此filter
很可能会按照集合提供的顺序调用元素的谓词。
我在这里专门谈论
stream()
,而不是parallelStream()
请注意,Stream<T>
可能无序并行。例如,在stream()
上调用unordered()
,结果会变得无序,但不会并行。
答案 3 :(得分:0)
如果从列表中创建了流,则可以保证收集的结果的排序方式与原始列表的排序方式相同,as the documentation states:
订购
如果订购了流,则大多数操作都被约束为对其遭遇顺序中的元素进行操作;如果流的源是包含[1,2,3]的List,那么执行map(x - &gt; x * 2)的结果必须是[2,4,6]。但是,如果源没有定义的遭遇顺序,那么值[2,4,6]的任何排列都将是有效的结果。
为了更进一步,我们无法保证执行map
执行的顺序。
在同一个文档页面中(在 副作用 段落中):
副作用
如果行为参数确实有副作用,除非明确说明,否则不能保证这些副作用对其他线程的可见性,也不保证对&#34;相同&#的不同操作34;同一个流管道中的元素在同一个线程中执行。此外,这些效果的排序可能令人惊讶。即使管道被约束产生的结果与流源的遭遇顺序一致(例如,IntStream.range(0,5).parallel()。map(x - &gt; x * 2).toArray ()必须产生[0,2,4,6,8]),不保证mapper函数应用于单个元素的顺序,或者对给定元素执行任何行为参数的线程。
实际上,对于有序的顺序流,可能会按顺序执行流操作,但不能保证。
答案 4 :(得分:0)
JLS是否有关于订单的保证 流操作是否应用于列表元素?
引自Ordering section in Stream javadocs
Streams可能有也可能没有已定义的遭遇顺序。是否 一个流有一个遭遇顺序取决于源和 中间操作。
将过滤谓词应用于“bc”之前不会发生 将过滤谓词应用于“a”?
如上所述,流可能有也可能没有已定义的顺序。但是在你的例子里,因为它是一个List,同样的Ordering section in Stream javadocs继续说
如果订购了流,则大多数操作都被限制为可以进行操作 他们遭遇顺序中的元素;如果流的来源是 列表包含[1,2,3],然后执行map的结果(x - &gt; x * 2) 必须是[2,4,6]。
将上述语句应用于您的示例 - 我相信,过滤谓词将按照List中定义的顺序接收元素。
或者,在将映射函数应用于“a”之前,不会将映射函数应用于“def”?
为此,我将参考流操作部分Stream operations in Streams,即
无状态操作(例如过滤器和地图)不保留任何状态 处理新元素时先前看过的元素
由于<ItemsControl ...>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Grid>
...
<ItemsPresenter/>
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{Binding SomeTextFromVM}"/>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
...
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
...
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
...
</ItemsControl.ItemContainerStyle>
</ItemsControl>
不保留状态,我相信,可以安全地假设在您的示例中“a”之前不会处理“def”。
1将在3之前打印?
尽管顺序流(如List)可能不太可能,但由于Ordering section in Stream javadocs确实表明
,因此无法保证某些终端操作可能会忽略遭遇顺序,例如 的forEach()。