如果找到的第一个元素为null,为什么findFirst()会抛出NullPointerException?

时间:2015-09-08 20:36:17

标签: java java-8 java-stream optional

为什么抛出java.lang.NullPointerException

List<String> strings = new ArrayList<>();
        strings.add(null);
        strings.add("test");

        String firstString = strings.stream()
                .findFirst()      // Exception thrown here
                .orElse("StringWhenListIsEmpty");
                //.orElse(null);  // Changing the `orElse()` to avoid ambiguity

strings中的第一项是null,这是一个完全可以接受的值。此外,findFirst()会返回Optional,这使findFirst()能够处理null更有意义。

编辑:将orElse()更新为不那么模糊。

5 个答案:

答案 0 :(得分:60)

原因是在回报中使用Optional<T>。可选不允许包含null。从本质上讲,它无法区分情况“它不在那里”和“它在那里,但它被设置为null”。

这就是the documentationnull中选择findFirst()时明确禁止这种情况的原因:

  

<强>抛出:

     

NullPointerException - 如果所选元素为null

答案 1 :(得分:39)

作为already discussed,API设计人员不会假设开发人员希望以相同的方式处理null值和缺少值。

如果您仍想这样做,可以通过应用序列

明确地执行此操作
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())

到流。如果没有第一个元素或第一个元素是null,则在两种情况下结果都是空的可选项。所以在你的情况下,你可以使用

String firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().flatMap(Function.identity())
    .orElse(null);
如果第一个元素不存在或null,则

获取null值。

如果您想区分这些情况,可以省略flatMap步骤:

Optional<String> firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().orElse(null);
System.out.println(firstString==null? "no such element":
                   firstString.orElse("first element is null"));

这与您更新的问题没什么不同。您只需将"no such element"替换为"StringWhenListIsEmpty",将"first element is null"替换为null。但如果你不喜欢条件,你也可以实现它:

String firstString = strings.stream().skip(0)
    .map(Optional::ofNullable).findFirst()
    .orElseGet(()->Optional.of("StringWhenListIsEmpty"))
    .orElse(null);

现在,firstString如果元素存在但null将是null,当没有元素存在时它将是"StringWhenListIsEmpty"

答案 2 :(得分:11)

以下代码将findFirst()替换为limit(1),并将orElse()替换为reduce()

String firstString = strings.
   stream().
   limit(1).
   reduce("StringWhenListIsEmpty", (first, second) -> second);

limit()只允许1个元素到达reduce。传递给BinaryOperator的{​​{1}}会返回1个元素,如果没有元素到达reduce,则返回"StringWhenListIsEmpty"

此解决方案的优点是reduce未分配,Optional lambda不会分配任何内容。

答案 3 :(得分:6)

您可以在找到

之前使用java.util.Objects.nonNull过滤列表

类似

list.stream().filter(Objects::nonNull).findFirst();

答案 4 :(得分:1)

可选应该是&#34;值&#34;类型。 (阅读javadoc中的细则:) JVM甚至可以用Optional<Foo>替换所有Foo,从而消除所有装箱和拆箱费用。 null Foo表示空Optional<Foo>

可以设计允许Optional with null值,而不添加布尔标志 - 只需添加一个sentinel对象。 (甚至可以使用this作为哨兵;请参阅Throwable.cause)

Optional无法包装null的决定不基于运行时成本。这是一个非常有争议的问题,您需要挖掘邮件列表。这个决定并不能令所有人信服。

在任何情况下,由于Optional不能包装null值,因此它会在findFirst之类的情况下将我们推向一个角落。他们必须有理由认为空值非常罕见(甚至认为Stream应该禁止空值),因此在空值而不是空流上抛出异常会更方便。

解决方法是框null,例如

class Box<T>
    static Box<T> of(T value){ .. }

Optional<Box<String>> first = stream.map(Box::of).findFirst();

(他们说每个OOP问题的解决方案是引入另一种类型:)