使用(扁平)映射相比简单的空值检查有哪些优势?

3
我读了下面的源码,很疑惑为什么要使用flatMap方法。我认为这样实例化的对象更多,执行的代码也更多,与简单的if语句的空值检查相比,速度更慢,而且在第一个null处终止,不必检查其他值,而且适用于包装器。
在我看来,if检查更快+更安全(对于我来说,速度真的很关键,因为我通常只有2-3毫秒来执行大量代码,如果有的话)。
使用“(flat)Map”可选方式的优点是什么?为什么应该考虑切换到它?
来源:http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
class Outer {
    Nested nested;
}

class Nested {
    Inner inner;
}

class Inner {
    String foo;
}

为了解决外部实例的内部字符串 "foo",你需要添加多个空值检查以防止可能的NullPointerExceptions。
Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
    System.out.println(outer.nested.inner.foo);
}

可以利用可选项的flatMap操作实现相同的行为:
Optional.of(new Outer())
    .flatMap(o -> Optional.ofNullable(o.nested))
    .flatMap(n -> Optional.ofNullable(n.inner))
    .flatMap(i -> Optional.ofNullable(i.foo))
    .ifPresent(System.out::println);

2
我会使用https://dev59.com/YFsW5IYBdhLWcg3wI0b7,即不使用`flatMap`而是使用`map`... - Tunaki
1
在我看来,这只是一个展示如何链式使用 flatMap 的例子。 - Alex Salauyou
2
@MichaelDibbets 这就是问题所在。首先,flatMap绝对不是正确的操作。如果有疑问,那就在map和简单地进行null检查之间选择。而这是基于个人观点的。 - Tunaki
1
对我来说,创建Optional实例并使用神奇的流操作符比仅进行布尔检查而没有额外开销更高效的可能性非常小。 - EpicPandaForce
1
我会根据上下文来做出选择...但无论如何,这是基于个人观点的。这类问题不适合在Stack Overflow上提问。 - Tunaki
显示剩余7条评论
1个回答

6

我认为在更广泛的流上下文中使用Optional会更清晰,而不是只有一行。

假设我们处理一个名为itemsOutersArrayList,要求获取存在的foo字符串的流。

我们可以这样做:

//bad example, read on
Stream<String> allFoos = list.stream()
            .filter(o -> o != null && o.nested != null && o.nested.inner != null)
            .map(o -> o.nested.inner.foo);

但我不得不重复说明如何从外部获取字符串 (o != null && o.nested != null && o.nested.inner != nullo.nested.inner.foo)。

Stream<String> allFoos =
        list.stream()
                .map(o -> Optional.ofNullable(o)
                        .map(t -> t.nested)
                        .map(n -> n.inner)
                        .map(i -> i.foo))
                .filter(s -> s.isPresent())
                .map(s -> s.get());

这还可以轻松地插入默认值:
Stream<String> allFoos =
            list.stream()
                    .map(o -> Optional.ofNullable(o)
                            .map(t -> t.nested)
                            .map(n -> n.inner)
                            .map(i -> i.foo)
                            .orElse("Missing"));

另一种选择可能看起来像这样:

//bad example (IMO)
Stream<String> allFoos = list.stream()
            .map(o -> o != null && o.nested != null && o.nested.inner != null ?
                    o.nested.inner.foo : "Missing");

你说的“我不得不重复自己”是什么意思? - Tschallacka
1
那是一个使用案例,当然。 - weston
1
它还为您提供了关于空值的类型安全性,例如,我无法以错误的顺序获取平面图,这将无法编译通过,但我可能会以错误的顺序进行 != null 的测试。 - weston
1
然而,一个好的测试套件可以使类型安全和我关于不正确的空检查顺序的观点变得无关紧要。http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.html - weston
1
@weston,您无需使用 .flatMap(t -> Optional.ofNullable(t.nested) 或其他类似的方法来链接Optional。您只需要像这样使用 map 方法即可:.map(t -> t.nested)。如果 t.nested 为空,则 map 方法将返回一个空的Optional。 - marstran
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接