我有一个元素列表,我需要找到满足条件的第一个元素,然后使用 Java 8 流退出。
我认为下面的代码不幸地评估了所有不是我需要的可用元素,我需要一个一个地评估项目并在找到第一个匹配项时停止(break
):
我在这里对元素进行排序,然后将元素映射到它的 url
属性然后尝试过滤如果 url
不为 null 或为空然后首先找到
匹配!
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
如输出所示,当过滤器与第一个元素匹配时,流不会停止,而是继续评估第二个元素!
我想这样做:
Arrays.sort(dataArray, Comparator.comparing(d -> d.getPriority())); // sort
for (Data data : dataArray) {
String url = data.getOriginalURL(shortUrl); // map
if (url != null && !url.isEmpty()) { // filter
System.out.println("url :" + url);
return url; // find first
}
}
最佳答案
这是一个说明问题的小例子:
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
如果 sorted
行未注释,则输出为:
PEEK: ab
PEEK: abc
PEEK: abcd
(如预期的那样,整个流水线的最终结果在两种情况下都是“ab”。)
的确,sorted
必须在生成第一个输出元素之前消耗所有输入。从这个意义上说,它是渴望的。但是,它会影响元素向下游发送的方式,这看起来确实很奇怪。
如果不进行排序,findFirst
操作会从上游“拉取”元素,直到找到一个元素,然后停止。通过排序,sorted()
操作急切地收集所有元素,对它们进行排序,并且由于它们都在那里,它会将它们“推”到流中。当然,findFirst
会忽略除第一个元素以外的所有元素。但这意味着干预操作(例如过滤器)可能会做不必要的工作。
最终的结果是正确的,但是行为是出乎意料的。这可能被认为是一个错误。如果合适,我会调查并提交错误。
关于java - 在 findFirst() 之前使用 sorted() 流式传输不再懒惰,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23419223/