java 8 findFirst vs map on optional

时间:2017-07-07 07:13:44

标签: java nullpointerexception java-8 java-stream optional

鉴于此代码:

class Foo {
  Integer attr;
  public Integer getAttr() {return attr;}
}

List<Foo> list = new ArrayList<>();
list.add(new Foo());

list.stream().map(Foo::getAttr).findAny().orElse(null);  //A
list.stream().findAny().map(Foo::getAttr).orElse(null);  //B

A行抛出

  

java.lang.NullPointerException:null

而B行返回null。

这种行为的原因是什么? findAny()map()都返回Optional<T>

4 个答案:

答案 0 :(得分:6)

list.stream().map(Foo::getAttr).findAny().orElse(null);

Java doc for streams表示Stream:&#34;返回一个流,该流包含将给定函数应用于此流的元素的结果&#34;,而findAny()&#34;可能返回aNullPointerException - 如果选中的元素为null&#34;。在类Foo中,默认情况下,Integer(非int)设置为null,因为已声明但未初始化。请参阅Primitives see default valuesObject initialization in Java

初始化不同于: A)类成员(对象和原语) B)局部变量

答案 1 :(得分:4)

显然,由于您执行这些操作的顺序以及findAny明确说明:throws NullPointerException if the element selected is null

当您执行map(Foo::getAttr)时,您已有效地将其映射到null,因此您的Stream现在包含null;因此findAny打破了一个异常(因为findAny应用于该空)

其他操作首先找到Foo对象,然后将其映射到Foo::getAttr(从而将其映射到Optional.empty()),从而调用orElse

此外,这对我来说更有意义(至少对我而言):

 list.stream()
     .findAny()
     .flatMap(f -> Optional.ofNullable(f.getAttr()))
     .orElse(null);

flatMap将映射到Optional<Integer>(属性),以防此empty获得orElse结果。

答案 2 :(得分:3)

首先,您的两个代码段map是不同的操作:

//            v--- stream intermediate operation
list.stream().map(Foo::getAttr).findAny().orElse(null);  //A
//                      v---- a Optional utility method 
list.stream().findAny().map(Foo::getAttr).orElse(null);  //B

并且NullPointerException出现在Stream#findAny操作中,因为它无法接受null值。由于它使用Optional.of而不是Optional.ofNullableStream#findAny的文档已经断言:

  

<强>抛出

     

NullPointerException - 如果所选元素为 null

因此,如果您希望A代码段工作正常,则必须在调用Stream#findAny之前过滤所有null值,例如:

//when no elements in stream, `findAny` will be return a empty by Optional.empty()
//                                                       v   
list.stream().map(Foo::getAttr).filter(Objects::nonNull).findAny().orElse(null);//A

答案 3 :(得分:0)

list.stream().map(Foo::getAttr)

...返回一个包含一个元素的流,其值为null。

findAny()(和findFirst())的JavaDoc说:

  

退货:

     

一个可选描述此流的某个元素,或者   empty如果流为空,则为可选

     

投掷:

     

NullPointerException - 如果选择的元素为null

所以findAny()完全按照文档记录进行操作:它选择了一个null,因此抛出NullPointerException

这是有道理的,因为Optional是(再次根据JavaDoc,但强调我的):

  

容器对象,可能包含也可能不包含非null

...这意味着您可以保证Optional.ifPresent( x -> x.method())永远不会因NullPointerException为空而抛出x

因此findAny()无法返回Optional.of(null)。而Optional.empty()表示流是空的,而不是它找到了空。

Stream / Optional基础架构的许多部分都是为了阻止使用空值。

您可以通过将空值映射到Optionals来产生Optional<Optional<Foo>> - 这看起来有点复杂,但它是您域的准确表示。 Optional.empty()表示流为空。 Optional.of(Optional.empty())表示找到一个空元素:

list.stream().map(Foo::getAttr).map(Optional::ofNullable).findAny()