Java findFirst()之前带有sorted()的流不再是惰性的
我有一个元素列表,我需要找到满足条件的第一个元素,然后使用Java8流退出 我认为以下代码不幸地评估了所有不符合我需要的可用元素,我需要逐个评估项目,并在找到第一个匹配项时停止(Java findFirst()之前带有sorted()的流不再是惰性的,java,java-8,java-stream,Java,Java 8,Java Stream,我有一个元素列表,我需要找到满足条件的第一个元素,然后使用Java8流退出 我认为以下代码不幸地评估了所有不符合我需要的可用元素,我需要逐个评估项目,并在找到第一个匹配项时停止(break): 我在这里对元素进行排序,然后将元素映射到其url属性,然后尝试筛选url是否为空,然后首先查找匹配项 Arrays.stream(dataArray) .sorted(Comparator.comparing(d -> d.getPriority())) .peek(o -> System.o
break
):
我在这里对元素进行排序,然后将元素映射到其url
属性,然后尝试筛选url
是否为空,然后首先查找匹配项
Arrays.stream(dataArray)
.sorted(Comparator.comparing(d -> d.getPriority()))
.peek(o -> System.out.println("SORT: " + o))
.map(d -> d.getOriginalURL(shortUrl))
.peek(o -> System.out.println("MAP: " + o))
.filter(u -> u != null && !u.isEmpty())
.peek(o -> System.out.println("FILTER: " + o))
.findFirst().orElse("");
但输出显示,即使第一项与if
条件(filter
)操作匹配,也会计算所有项
Data[] data = new Data[] { new ParseData(), new InMemoryData() };
System.out.println(">>> " + getOriginalURL(data, ""));
输出:
SORT: mhewedy.usingspark.data.InMemoryData@7adf9f5f
MAP: InMemory URL
FILTER: InMemory URL
SORT: mhewedy.usingspark.data.ParseData@85ede7b
MAP: Parse.com URL <<< THIS SHOULD NOT HAPPEN
FILTER: Parse.com URL <<< AND THIS TOO
>>> InMemory URL
排序的操作强制遍历流中的所有项
有状态操作(如distinct和sorted)可以包含
处理新元素时来自以前看到的元素的状态
有状态操作可能需要在执行之前处理整个输入
产生结果。例如,无法从中产生任何结果
对流进行排序,直到看到流的所有元素
但是,我不确定为什么对流中的所有元素执行排序后的操作
如果单独执行排序,然后将流用于其余的处理,则处理将在找到第一个匹配项时停止,如预期的那样
Arrays.sort(dataArray, Comparator.comparing(d -> d.getPriority())); // sort
Arrays.stream(dataArray)
.peek(o -> System.out.println("SORT: " + o))
.map(d -> d.getOriginalURL(shortUrl))
.peek(o -> System.out.println("MAP: " + o))
.filter(u -> u != null && !u.isEmpty())
.peek(o -> System.out.println("FILTER: " + o))
.findFirst().orElse("");
下面是一个较小的示例,说明了这个问题:
Stream.of("a", "ab", "abc", "abcd")
// .sorted() // uncomment and what follows becomes eager
.filter(s -> s.contains("b"))
.peek(s -> System.out.println("PEEK: " + s))
.findFirst()
.orElse("X");
正如预期的那样,输出为:
PEEK: ab
PEEK: ab
PEEK: abc
PEEK: abcd
如果排序的行未注释,则输出为:
PEEK: ab
PEEK: ab
PEEK: abc
PEEK: abcd
(正如预期的那样,在这两种情况下,整个管道的最终结果都是“ab”。)
确实,排序后的必须在生成第一个输出元素之前消耗所有输入。从这个意义上说,它是渴望的。然而,它确实会影响元素被发送到下游的方式,这看起来很奇怪
如果不进行排序,findFirst
操作从上游“拉”元素,直到找到一个元素,然后停止。通过排序,sorted()
操作急切地收集所有元素,对它们进行排序,因为它们都在那里,所以它会将它们“推”到流中。当然,findFirst
会忽略除第一个元素以外的所有元素。但这意味着干预操作(如过滤器)可能会做不必要的工作
最终结果是正确的,但行为是意外的。这可能被认为是一个bug。如果合适,我将调查并提交一个bug。您想要使用的是stream.collect(minBy(…)
。它具有线性性能
minBy
静态方法可以在Collectors
类中找到。我同意你的观点,尤其是如果任何中间
流操作是昂贵的操作。(在我的问题中,getOriginalURL
是一个昂贵的!是的,性能错误。归档。@MuhammadHewedy很好的捕获,顺便说一句。谢谢你提出这个问题。@StuartMarks我很好奇-你怎么知道它是一个错误?你检查过这个行为没有其他逻辑解释吗?@Eran,我看了源代码(,第301-308行)查看排序后它是否无条件地将所有元素向下推。这似乎是错误的,因为findFirst
应该是短路。然后我问实现它的人,他说:“是的,这是一个性能缺陷。”:-)