Java8流-排序方法的执行标准不同于过滤器和映射方法

Java8流-排序方法的执行标准不同于过滤器和映射方法,java,filter,java-8,stream,java-stream,Java,Filter,Java 8,Stream,Java Stream,这个问题与 我正在使用Java8流的排序、过滤和映射方法。 考虑到上述问题答案中指定的过滤器和映射的工作方式,我尝试了如下排序方法: Stream.of("d2", "a2", "b1", "b3", "c") .sorted((s1, s2) -> { System.out.printf("sort: %s; %s\n", s1, s2); return s1.compareTo(s2); }) .filter(s -> { System.out.print

这个问题与

我正在使用Java8流的排序、过滤和映射方法。 考虑到上述问题答案中指定的过滤器和映射的工作方式,我尝试了如下排序方法:

Stream.of("d2", "a2", "b1", "b3", "c")
.sorted((s1, s2) -> {
    System.out.printf("sort: %s; %s\n", s1, s2);
    return s1.compareTo(s2);
})
.filter(s -> {
    System.out.println("filter: " + s);
    return s.startsWith("a");
})
.map(s -> {
    System.out.println("map: " + s);
    return s.toUpperCase();
})
.forEach(s -> System.out.println("forEach: " + s));
我得到的结果是:

排序:a2;d2 类别:b1;a2 类别:b1;d2 类别:b1;a2 排序:b3;b1 排序:b3;d2 排序:c;b3 排序:c;d2 过滤器:a2 地图:a2 收件人:A2 过滤器:b1 过滤器:b3 过滤器:c 过滤器:d2

也就是说,现在对完整循环执行sorted方法,然后对单个项执行filter和map函数。 由于这三个函数都是中间函数,所以它们都应该以相同的方式工作。 执行令罚款与否?我没有发现我做错了什么。

“这要看情况而定”。流操作可以交错,因为每个阶段都可以在项目顺序明确时立即发出项目。在过滤和映射的情况下,可以处理和传递每个项目

在排序的情况下,这完全取决于排序算法。如果排序实现为“查找具有n-1个比较的所有元素的最小值,发出最小值,与其余元素重复”,则排序确实会与过滤和映射交错。但从您的输出来看,它更像是一种插入排序(使用二进制搜索或插入点的某种搜索树):a2/d2比较结果(a2,d2);首先将b1与a2进行比较,然后将d2插入两个屈服点之间(a2,b1,d2),将b3与b1进行比较,然后将d2插入(a2,b1,b3,d2),依此类推。 这是有意义的,因为它会产生一个用于排序的预期O(nlogn)时间(与重复最小查找的O(n^2)时间相比),但在插入最后一个元素之前,它不会发出任何东西。 也就是说,必须先完成排序,然后才能开始筛选。

副作用

通常不鼓励行为参数对流操作产生副作用,因为它们通常会导致无意中违反无状态性要求,以及其他线程安全危险

[……]此外,这些效应的顺序可能令人惊讶。即使管道被约束以生成与流源的相遇顺序一致的结果(例如,
IntStream.range(0,5).parallel().map(x->x*2).toArray()必须生成[0,2,4,6,8])
,也不能保证映射器函数应用于单个元素的顺序,或者在哪个线程中为给定元素执行任何行为参数


如何对元素进行排序,而不让所有元素都看到它们?与
distinct()
相同。它仍然是懒惰的——如果
forEach
不存在,
sorted
操作将不会被调用。@KaranVerma你真的应该停止问那么多问题,读一些东西。。是的,这是回答和回答的同一个链接。回答你所有问题的神奇的东西叫做官方文件。@the8472:讽刺的是,它从来没有这样做过,尽管它在过去过于激进。据说,在未来的版本中也不太可能这样做,因为排序后的和无序的终端操作的组合很可能是一个没有正确行为的使用错误(以及为什么要优化错误案例)。因此,在并行流中使用它最终会浪费排序时间,同时仍然以任意顺序调用消费者…@the8472:我看不到“默认排序”中的值。如果消费者对排序结果感兴趣,他可以链接排序操作。必须选择不浪费资源听上去不是一个好模式。哦,澄清我的答案:你没有做错任何事。我的回答应该帮助你接受并理解为什么你的输出是完全合理的。。。这既有趣又奇怪