为什么抛出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()
更新为不那么模糊。
答案 0 :(得分:60)
原因是在回报中使用Optional<T>
。可选不允许包含null
。从本质上讲,它无法区分情况“它不在那里”和“它在那里,但它被设置为null
”。
这就是the documentation在null
中选择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问题的解决方案是引入另一种类型:)