Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does findFirst() throw a NullPointerException if the first element it finds is null?

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 nulls.

EDIT: updated the orElse() to be less ambiguous.

like image 408
neverendingqs Avatar asked Sep 08 '15 20:09

neverendingqs


People also ask

Can findFirst return null?

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 .

What can help us in avoiding NullPointerException and null checks in Java?

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.

What is optional of null?

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.


2 Answers

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 is null

like image 96
Sergey Kalinichenko Avatar answered Sep 19 '22 14:09

Sergey Kalinichenko


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.

like image 44
Holger Avatar answered Sep 18 '22 14:09

Holger