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

为什么这会抛出一个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来说更为findFirst()

编辑:更新orElse()更不明确。

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

这就是为什么当在findFirst()selectnull时,文档明确禁止这种情况:

抛出:

NullPointerException – 如果select的元素为null

如前所述,APIdevise者不会假设开发者想要以相同的方式处理null值和缺失值。

如果你仍然想这样做,你可以通过应用序列明确地做到这一点

 .map(Optional::ofNullable).findFirst().flatMap(Function.identity()) 

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

 String firstString = strings.stream() .map(Optional::ofNullable).findFirst().flatMap(Function.identity()) .orElse(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")); 

这与您更新的问题没有多大区别。 你只需用"StringWhenListIsEmpty""first element is null"replace为"no such element" 。 但是,如果你不喜欢条件,你可以实现它也像:

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

现在,如果元素存在,则firstString将为null ,但为null ,如果元素不存在,则为"StringWhenListIsEmpty"

以下代码用limit(1)replacefindFirst() ,并用orElse()replaceorElse() reduce()

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

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

这个解决scheme的BinaryOperatorOptional不分配和BinaryOperator lambda不会分配任何东西。

您可以使用java.util.Objects.nonNull在查找之前过滤列表

就像是

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

可选应该是一个“价值”types。 (请阅读javadoc中的细则 🙂 JVM甚至可以用Optional<Foo>replace所有Optional<Foo> ,从而消除所有装箱和拆箱成本。 null Foo表示一个空的Optional<Foo>

这是一个可能的devise,允许可选的空值,而不添加布尔标志 – 只需添加一个哨兵对象。 (甚至可以用this作为标记;参见Throwable.cause)

可选不能包含null的决定不是基于运行时成本。 这是一个非常有争议的问题,你需要挖掘邮件列表。 这个决定对每个人都没有说服力。

在任何情况下,由于Optional不能包装null值,所以它findFirst这样的情况推到一个angular落。 他们必须推断null值非常罕见(甚至认为Stream应禁止空值),因此在null值而不是空值上抛出exception更为方便。

一个解决方法是框为null ,例如

 class Box<T> static Box<T> of(T value){ .. } Optional<Box<String>> first = stream.map(Box::of).findFirst(); 

(他们说,每个OOP问题的解决scheme是引入另一种types:)