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

 霸气的gmail 发布于 2023-01-17 21:06

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

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

我在这里排序元素,然后将元素映射到它的url属性,然后尝试过滤,如果url不是null或空,然后找到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("");

但输出显示,即使第一个项与ifcondition(filter)操作匹配,所有项也都会被评估.

Data[] data = new Data[] { new ParseData(), new InMemoryData() };
System.out.println(">>> " + getOriginalURL(data, ""));

OUTPUT:

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

Stuart Marks.. 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

(正如预期的那样,在这两种情况下,整个管道的最终结果都是"ab".)

确实,sorted在生成第一个输出元素之前必须消耗所有输入.从那个意义上来说,它是渴望的.但是,它确实会影响元素向下游发送的方式.

如果没有排序,findFirst操作会从上游"拉"元素,直到找到元素,然后停止.通过排序,sorted()操作急切地收集所有元素,对它们进行排序,并且由于它们在那里完成它们,它会将它们"推"到流中.当然,findFirst忽略除第一个元素之外的所有元素.但这意味着干预操作(例如过滤器)可能会做不必要的工作.

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

1 个回答
  • 这是一个较小的例子来说明问题:

    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忽略除第一个元素之外的所有元素.但这意味着干预操作(例如过滤器)可能会做不必要的工作.

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

    2023-01-17 21:12 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有