【问题标题】:type of map() and filter() operations of OptionalOptional 的 map() 和 filter() 操作的类型
【发布时间】:2017-05-26 12:46:09
【问题描述】:

Optionalmap()filter() 是否像 Stream 一样懒惰?

我如何确认他们的类型?

【问题讨论】:

  • 可以通过在地图或过滤器表达式中记录一些内容来轻松验证这一点,然后检查记录这些内容的时间。
  • 一行代码会更有帮助:)
  • 不,最有帮助的是你自己弄清楚,这样你学得最多。
  • @MehrajMalik 我的回答很糟糕(除了完全错误之外),请接受您的另一个回答。

标签: java java-stream lazy-evaluation eager


【解决方案1】:

StreamOptional 之间存在根本区别。

Stream 封装了整个处理管道,在执行任何操作之前收集所有操作。这允许实现选择不同的处理策略,具体取决于实际请求的结果。这也允许在链中插入unordered()parallel() 之类的修饰符,因为到目前为止,还没有做任何事情,因此我们可以更改后续实际处理的行为。

一个极端的例子是Stream.of(1, 2, 3).map(function).count(),它在Java 9中根本不会处理function,因为没有3的不变结果可以确定。

相比之下,Optional 只是一个值的包装器(如果不是空的)。每个操作都将立即执行,以返回封装新值的新Optional 或空Optional。在 Java 8 中,所有返回 Optional 的方法,即mapflatMapfilter,在应用于空可选项时只会返回空可选项,因此在链接它们时,空可选项变成了一种死路一条。

但 Java 9 将引入 Optional<T> or​(Supplier<? extends Optional<? extends T>>),当应用于空的可选项时,它可能会从供应商处返回一个非空的可选项。

由于Optional 代表一个(可能不存在)值,而不是处理管道,您可以根据需要多次查询相同的Optional,无论查询返回新的Optional 还是最终值。

很容易验证。以下代码

Optional<String> first=Optional.of("abc");
Optional<String> second=first.map(s -> {
    System.out.println("Running map");
    return s + "def";
});
System.out.println("starting queries");
System.out.println("first: "+(first.isPresent()? "has value": "is empty"));
System.out.println("second: "+(second.isPresent()? "has value": "is empty"));
second.map("second's value: "::concat).ifPresent(System.out::println);

将打印

Running map
starting queries
first: has value
second: has value
second's value: abcdef

证明在任何其他查询之前立即评估映射函数,并且我们仍然可以在通过map 创建第二个选项后查询first 选项并多次查询选项。

事实上,强烈建议先通过isPresent()进行检查,然后再调用get()

没有等效的流代码,因为以这种方式重用Stream 实例是无效的。但是我们可以证明在终端操作开始之前没有执行中间操作:

Stream<String> stream=Stream.of("abc").map(s -> {
    System.out.println("Running map");
    return s + "def";
});
System.out.println("starting query");
Optional<String> result = stream.findAny();
System.out.println("result "+(result.isPresent()? "has value": "is empty"));
result.map("result value: "::concat).ifPresent(System.out::println);

将打印

starting query
Running map
result has value
result value: abcdef

显示在终端操作findAny() 开始之前未评估映射函数。由于我们不能多次查询一个流,findAny() 甚至使用Optional 作为返回值,这允许我们对最终结果执行此操作。


同名操作之间还有其他语义差异,例如如果映射函数的计算结果为 nullOptional.map 将返回一个空的 Optional。对于流,传递给map 的函数返回null 还是非null 值没有区别(这就是为什么我们可以在不知道是否返回的情况下计算元素)。

【讨论】:

    【解决方案2】:
     String r = Optional.of("abc")
                .map(s -> {
                    System.out.println("Running map");
                    return s + "def";
                })
                .filter(s -> {
                    System.out.println("First Filter");
                    return s.equals("abcdef");
                })
                .map(s -> {
                    System.out.println("mapping");
                    return s + "jkl";
                })
                .orElse("done");
    
        System.out.println(r);
    

    运行它会产生:

    运行map,First Filter,映射abcdefjkl

    另一方面,运行这个:

    String r = Optional.of("mnt") //changed
                .map(s -> {
                    System.out.println("Running map");
                    return s + "def";
                })
                .filter(s -> {
                    System.out.println("First Filter");
                    return s.equals("abcdef");
                })
                .map(s -> {
                    System.out.println("mapping");
                    return s + "jkl";
                })
                .orElse("done");
    

    运行地图,第一个过滤器,完成

    我一直以为map只是在之前的filter的基础上执行的,这会被认为是lazy。事实证明这是不正确

     Optional.of("s").map(String::toUpperCase)
     Stream.of("test").map(String::toUpperCase)
    

    来自Optionalmap 将被执行;而来自Stream 的则不会。

    编辑

    去这里投票另一个答案。这是因为另一个而被编辑的。

    【讨论】:

    • 三天无异议……您所证明的只是,空的Optional 不会评估映射函数。这不需要被认为是“流道”。想想HashSet……在你删除了所有元素之后,处理所有元素的方法变成了无操作。它并不能证明这些操作是急切的还是懒惰的。 Optional 操作不是懒惰的这一事实最好地说明了这一点
    • @Holger 我有一个提示我错过了一些东西,谢谢你回到这里。我一直认为 Optional 上的 filter.filter 会算作惰性,因为第二个过滤器不是基于第一个过滤器进行评估的。
    • @Holger 重新阅读问题和答案。我是个白痴,谢谢你证明我错了。
    猜你喜欢
    • 2021-12-19
    • 2021-06-16
    • 1970-01-01
    • 2014-10-28
    • 2019-08-21
    • 1970-01-01
    • 2023-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多