Java 如果 findFirst() 找到的第一个元素为空,为什么会抛出 NullPointerException?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32466799/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does findFirst() throw a NullPointerException if the first element it finds is null?
提问by neverendingqs
Why does this throw a java.lang.NullPointerException
?
为什么这会抛出一个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
The first item in strings
is null
, which is a perfectly acceptable value. Furthermore, findFirst()
returns an Optional, which makes even more sense for findFirst()
to be able to handle null
s.
中的第一项strings
是null
,这是一个完全可以接受的值。此外,findFirst()
返回一个Optional,这对于findFirst()
能够处理null
s更有意义。
EDIT: updated the orElse()
to be less ambiguous.
编辑:更新orElse()
为不那么模棱两可。
采纳答案by dasblinkenlight
The reason for this is the use of Optional<T>
in the return. Optional is not allowed to contain null
. Essentially, it offers no way of distinguishing situations "it's not there" and "it's there, but it is set to null
".
这样做的原因是Optional<T>
在返回中使用了。Optional 不允许包含null
. 从本质上讲,它无法区分“它不在那里”和“它在那里,但它被设置为null
”的情况。
That's why the documentationexplicitly prohibits the situation when null
is selected in findFirst()
:
这就是为什么文档明确禁止在以下情况下null
选择的情况findFirst()
:
Throws:
NullPointerException
- if the element selected isnull
抛出:
NullPointerException
- 如果选择的元素是null
回答by ZhongYu
Optional is supposed to be a "value" type. (read the fine print in javadoc:) JVM could even replace all Optional<Foo>
with just Foo
, removing all boxing and unboxing costs. A null
Foo means an empty Optional<Foo>
.
Optional 应该是“值”类型。(阅读javadoc中的细则:) JVM 甚至可以将 all 替换Optional<Foo>
为 just Foo
,从而消除所有装箱和拆箱成本。一个null
Foo 意味着一个空的Optional<Foo>
。
It is a possible design to allow Optional with null value, without adding a boolean flag - just add a sentinel object. (could even use this
as sentinel; see Throwable.cause)
允许 Optional 具有空值是一种可能的设计,而无需添加布尔标志 - 只需添加一个哨兵对象。(甚至可以this
用作哨兵;参见 Throwable.cause)
The decision that Optional cannot wrap null is not based on runtime cost. This was a hugely contended issue and you need to dig the mailing lists. The decision is not convincing to everybody.
Optional 不能包装 null 的决定不是基于运行时成本。这是一个极具争议的问题,您需要挖掘邮件列表。这个决定并不能让所有人信服。
In any case, since Optional cannot wrap null value, it pushes us in a corner in cases like findFirst
. They must have reasoned that null values are very rare (it was even considered that Stream should bar null values), therefore it is more convenient to throw exception on null values instead of on empty streams.
在任何情况下,由于 Optional 不能包装空值,它在像findFirst
. 他们一定有理由认为空值非常罕见(甚至认为 Stream 应该禁止空值),因此对空值而不是空流抛出异常更方便。
A workaround is to box null
, e.g.
一种解决方法是装箱null
,例如
class Box<T>
static Box<T> of(T value){ .. }
Optional<Box<String>> first = stream.map(Box::of).findFirst();
(They say the solution to every OOP problem is to introduce another type :)
(他们说每个 OOP 问题的解决方案是引入另一种类型:)
回答by Holger
As already discussed, the API designers do not assume that the developer wants to treat null
values and absent values the same way.
正如已经讨论过的,API 设计者并不假设开发人员希望以null
相同的方式对待值和缺失值。
If you still want to do that, you may do it explicitly by applying the sequence
如果你仍然想这样做,你可以通过应用序列来明确地做到这一点
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
to the stream. The result will be an empty optional in both cases, if there is no first element or if the first element is null
. So in your case, you may use
到溪流。在这两种情况下,如果没有第一个元素或第一个元素是 ,结果将是一个空的可选项null
。所以在你的情况下,你可以使用
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
.orElse(null);
to get a null
value if the first element is either absent or null
.
得到一个null
值,如果第一个元素是不存在或null
。
If you want to distinguish between these cases, you may simply omit the flatMap
step:
如果你想区分这些情况,你可以简单地省略这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"));
This is not much different to your updated question. You just have to replace "no such element"
with "StringWhenListIsEmpty"
and "first element is null"
with null
. But if you don't like conditionals, you can achieve it also like:
这与您更新的问题没有太大不同。你只需要更换"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);
Now, firstString
will be null
if an element exists but is null
and it will be "StringWhenListIsEmpty"
when no element exists.
现在,firstString
将是null
如果一个元素存在,但null
它会"StringWhenListIsEmpty"
在没有元素存在。
回答by Nathan
The following code replaces findFirst()
with limit(1)
and replaces orElse()
with reduce()
:
以下代码替换findFirst()
为limit(1)
和替换orElse()
为reduce()
:
String firstString = strings.
stream().
limit(1).
reduce("StringWhenListIsEmpty", (first, second) -> second);
limit()
allows only 1 element to reach reduce
. The BinaryOperator
passed to reduce
returns that 1 element or else "StringWhenListIsEmpty"
if no elements reach the reduce
.
limit()
只允许 1 个元素到达reduce
。在BinaryOperator
传递到reduce
返回的是1元,否则"StringWhenListIsEmpty"
,如果没有元素到达reduce
。
The beauty of this solution is that Optional
isn't allocated and the BinaryOperator
lambda isn't going to allocate anything.
这个解决方案的美妙之处在于Optional
它没有被分配,而且BinaryOperator
lambda 不会分配任何东西。
回答by Mattos
You can use java.util.Objects.nonNull
to filter the list before find
您可以java.util.Objects.nonNull
在查找之前使用过滤列表
something like
就像是
list.stream().filter(Objects::nonNull).findFirst();