Why does this throw a 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.
EDIT: updated the orElse()
to be less ambiguous.
The findFirst method of Stream finds the first element as Optional in this stream. If stream has no element, findFirst returns empty Optional . If the stream has no encounter order then findFirst may select any element. If the selected element by findFirst is null, it throws NullPointerException .
Java 8 introduced an Optional class which is a nicer way to avoid NullPointerExceptions. You can use Optional to encapsulate the potential null values and pass or return it safely without worrying about the exception. Without Optional, when a method signature has return type of certain object.
Optional is a container object used to contain not-null objects. Optional object is used to represent null with absent value. This class has various utility methods to facilitate code to handle values as 'available' or 'not available' instead of checking null values.
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
".
That's why the documentation explicitly prohibits the situation when null
is selected in findFirst()
:
Throws:
NullPointerException
- if the element selected isnull
As already discussed, the API designers do not assume that the developer wants to treat null
values and absent values the same way.
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
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
.
If you want to distinguish between these cases, you may simply omit the flatMap
step:
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With