ass*_*ias 5 java java-8 java-stream
是否可以保证在使用流时,中间操作将按程序顺序执行?我怀疑是这种情况,或者它会导致非常微妙的错误,但我找不到明确的答案.
例:
List<String> list = Arrays.asList("a", "b", "c");
List<String> modified = list.parallelStream()
.map(s -> s + "-" + s) //"a-a", "b-b", "c-c"
.filter(s -> !s.equals("b-b")) //"a-a", "c-c"
.map(s -> s.substring(2)) //"a", "c"
.collect(toList());
Run Code Online (Sandbox Code Playgroud)
这保证总是返回["a", "c"]或["c", "a"]?(如果最后一个映射操作在第一个映射操作之前执行,则可能抛出异常 - 类似地,如果在第二个映射操作之后执行过滤器,则"b"将保留在最终列表中)
Stu*_*rks 10
关于原始问题中嵌入的排序实际上有几个问题.
Holger的答案涵盖了管道中流操作的顺序.对于特定的流元素,管道操作必须按程序中的描述执行,因为通常,类型必须匹配,并且因为它以任何其他方式执行它是没有意义的.从原始示例开始,流库无法对操作进行重新排序,就像它们已被写入一样,
List<String> modified = list.parallelStream()
.filter(s -> !s.equals("b-b")) // these two operations are swapped
.map(s -> s + "-" + s) // compared to the original example
.map(s -> s.substring(2))
.collect(toList());
Run Code Online (Sandbox Code Playgroud)
因为那时结果将是[a,b,c].这不会发生.
最初的问题是答案是[c,a]而不是[a,c].这实际上是关于不同类型的排序的问题,我们将其称为遭遇顺序.java.util.stream包文档中提到了这个概念.不幸的是,在我所知道的任何地方都没有明确定义.简而言之,它涉及流内元素的相对定位(与执行顺序相反)以及该定位是否具有任何语义.
例如,考虑源自HashSet和ArrayList的流.基于HashSet的流没有定义的遭遇顺序,换句话说,它是无序的.如果你将一堆元素放入一个HashSet然后迭代它们,它们会以某种顺序出现,这可能与你放入它们的顺序无关.
但是,基于List的流确实具有已定义的遭遇顺序.在原始示例中,列表是[a,b,c],并且显然"a"在"b"之前出现在"c"之前.这种定位通常由从源到输出的流操作保留.
让我修改原始示例以显示遭遇订单的重要性.我所做的就是改变原始列表中字符串的顺序:
List<String> list = Arrays.asList("c", "b", "a");
List<String> modified = list.parallelStream()
.map(s -> s + "-" + s) //"c-c", "b-b", "a-a"
.filter(s -> !s.equals("b-b")) //"c-c", "a-a"
.map(s -> s.substring(2)) //"c", "a"
.collect(toList());
Run Code Online (Sandbox Code Playgroud)
正如我们所料,输出为[c,a].现在让我们在一组而不是一个列表上运行流:
List<String> list = Arrays.asList("c", "b", "a");
Set<String> set = new HashSet<>(list);
List<String> modified = set.parallelStream()
.map(s -> s + "-" + s)
.filter(s -> !s.equals("b-b"))
.map(s -> s.substring(2))
.collect(toList());
Run Code Online (Sandbox Code Playgroud)
这次,结果是[a,c].该流水线作业(地图,过滤器,地图)并没有改变顺序,但自从遭遇顺序集合中的元素是不确定的,结果在发生这种情况从以前的结果不同,有的为了目的地列表中结束.
(我不得不改变原始列表中值的顺序,因为碰巧HashSet的迭代顺序与元素的哈希码有关,这里给出的简单字符串示例都有连续的哈希码.)
还有另一种可能考虑的"排序",即不同元素之间的管道操作的相对执行顺序.对于并行流,这是完全不确定的.观察这种情况的一种方法是在管道操作中改变对象.(为了安全地做到这一点,被突变的对象当然必须是线程安全的,依赖于任何这样的副作用的排序是不明智的.)这是一个例子:
List<Integer> list1 = Collections.synchronizedList(new ArrayList<>());
List<Integer> list2 =
IntStream.range(0, 10)
.parallel()
.boxed()
.peek(i -> list1.add(i))
.collect(toList());
System.out.println(list1);
System.out.println(list2);
Run Code Online (Sandbox Code Playgroud)
在我的系统上,输出是:
[5, 6, 2, 3, 4, 8, 9, 7, 0, 1]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Run Code Online (Sandbox Code Playgroud)
源的遭遇顺序保留在list2的输出中,但list1的顺序通常是不同的.实际上,元素的排序list1因运行而异,而元素的顺序list2始终相同.
总之,这里显示了三种不同的排序:
他们都是截然不同的.
您的问题出现了,因为您正在从一种类型映射到同一类型.如果您考虑正在执行的正式操作,很明显无法更改指定操作的顺序:
Stream<A>到B创建的任意类型Stream<B>Filter<B>第一个映射的结果Stream<B>到任意类型C创建Stream<C>C成List<C>查看这些正式步骤,应该清楚的是,由于类型兼容性要求,无法更改这些步骤的顺序.
事实上,在你的特殊情况下,所有三种类型恰好String都不会改变Streams工作方式的逻辑.请记住,用于类型参数的实际类型将被删除,并且在运行时不存在.
该Stream实现可能会强制将操作它是有用的,例如执行sorted和distinct一气呵成,但这需要两个操作都要求在同一项目和Comparator.或者简单地说,内部优化不得改变所请求操作的语义.
| 归档时间: |
|
| 查看次数: |
1188 次 |
| 最近记录: |