在findFirst()之前使用sorted()流不再是懒惰的

时间:2014-05-02 01:07:05

标签: java java-8 java-stream

我有一个元素列表,我需要找到满足条件的第一个元素,然后使用Java 8流退出。

我认为以下代码很遗憾地评估了所有可用的元素并不是我需要的东西,我需要逐个评估项目并在找到第一个匹配时停止(break):

我在这里对元素进行排序,然后将元素映射到其url属性,然后尝试过滤url不为空或空,然后找到first匹配!

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
    }
}

3 个答案:

答案 0 :(得分:12)

这是一个较小的例子来说明问题:

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

(整个管道的最终结果是&#34; ab&#34;在这两种情况下都是如预期的那样。)

在生成第一个输出元素之前,sorted必须消耗所有输入才是真的。从这个意义上来说,它是渴望的。但是,它似乎很奇怪,它会影响元素在下游的发送方式。

没有排序,findFirst操作&#34;拉动&#34;从上游到找到一个元素,然后停止。通过排序,sorted()操作急切地收集所有元素,对它们进行排序,并且因为它们可以在那里完成它,它会推动&#34;他们顺着溪流。当然,findFirst忽略了除第一个元素之外的所有元素。但这意味着干预操作(例如过滤器)可能会做不必要的工作。

最终结果是正确的,但行为是意外的。这可能被视为一个错误。如果合适,我会调查并提交错误。

答案 1 :(得分:4)

sorted操作强制遍历流中的所有项目。

  

可以包含有状态操作,例如distinct和sorted   在处理新元素时从先前看到的元素说明。

     

有状态操作可能需要先处理整个输入   产生一个结果。例如,一个人不能产生任何结果   对流进行排序,直到看到流的所有元素。

(Source)

我不确定为什么还会对流中的所有元素执行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("");

答案 2 :(得分:1)

您要使用的是stream.collect(minBy(...))。它具有线性性能。

minBy类上可以找到Collectors静态方法。