Why does findFirst() throw a NullPointerException if the first element it finds is null?
Solution 1
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
Solution 2
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.
Solution 3
You can use java.util.Objects.nonNull
to filter the list before find
something like
list.stream().filter(Objects::nonNull).findFirst();
Solution 4
The following code replaces findFirst()
with limit(1)
and replaces orElse()
with 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
.
The beauty of this solution is that Optional
isn't allocated and the BinaryOperator
lambda isn't going to allocate anything.
Solution 5
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>
.
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)
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.
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.
A workaround is to box null
, e.g.
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 :)
Related videos on Youtube
neverendingqs
I am adding an "About me" purely for the user card.
Updated on September 14, 2021Comments
-
neverendingqs over 2 years
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
isnull
, which is a perfectly acceptable value. Furthermore,findFirst()
returns an Optional, which makes even more sense forfindFirst()
to be able to handlenull
s.EDIT: updated the
orElse()
to be less ambiguous.-
Michele Lacorte over 8 yearsnull isn't perfectly acceptable value... use "" instead
-
neverendingqs over 8 years@MicheleLacorte although I'm using
String
here, what if it's a list that represents a column in the DB? The value of the first row for that column can benull
. -
Michele Lacorte over 8 yearsYes but in java null isn't acceptable..use query to set null into db
-
John Bollinger over 8 years@MicheleLacorte,
null
is a perfectly acceptable value in Java, generally speaking. In particular, it is a valid element for anArrayList<String>
. Like any other value, however, there are limitations on what can be done with it. "Don't ever usenull
" is not useful advice, as you cannot avoid it. -
neverendingqs over 8 years@NathanHughes - I suspect that by the time you call
findFirst()
, there's nothing else you want to do.
-
-
neverendingqs over 8 yearsIt would be simple to track if a value does exist with a private boolean inside instances of
Optional
. Anyway, I think I'm bordering ranting - if the language doesn't support it, it doesn't support it. -
neverendingqs over 8 yearsAlso, I would be happy if you have any alternatives (was hoping for a functional programming-like solution). Time to convert it to a for loop.
-
Sergey Kalinichenko over 8 years@neverendingqs Absolutely, using a
boolean
to differentiate these two situations would make perfect sense. To me, it looks like the use ofOptional<T>
here was a questionable choice. -
Sergey Kalinichenko over 8 years@neverendingqs I can't think of any nice-looking alternative to this, besides rolling your own null, which isn't ideal either.
-
neverendingqs over 8 yearsI ended up writing a private method that gets the iterator from any
Iterable
type, checkshasNext()
, and returns the appropriate value. -
Holger over 8 yearsThere is no need to create another
Box
type. TheOptional
type itself can serve this purpose. See my answer for an example. -
neverendingqs over 8 yearsSorry I realized that my question might have implied I wanted to return
null
for either 1) the first element isnull
or 2) no elements exist inside the list. I have updated the question to remove the ambiguity. -
ZhongYu over 8 years@Holger - yes, but that might be confusing since it's not the intended purpose of Optional. In OP's case,
null
is a valid value like any other, no special treatment for it. (until sometime later:) -
ZhongYu over 8 yearsIn the 3rd code snippet, an
Optional
may be assigned tonull
. SinceOptional
is supposed to be a "value type", it should never be null. And an Optional should never be compared by==
. The code may fail in Java 10 :) or when-ever value type is introduced to Java. -
Holger over 8 years@bayou.io: the documentation doesn’t say that references to value types can’t be
null
and while instances should never be compared by==
, the reference may be tested fornull
using==
as that’s the only way to test it fornull
. I can’t see how such a transition to “nevernull
” should work for existing code as even the default value for all instance variables and array elements isnull
. The snippet surely is not the best code but neither is the task of treatingnull
s as present values. -
ZhongYu over 8 yearssee john rose - nor can it be compared with the “==” operator, not even with a null
-
ZhongYu over 8 yearsanyways, this is their plan, that
Optional
will not be used like "normal" reference types. so that in future, it can be transitioned to "value type". But people will definitely "misuse" it; the warning on javadoc is certainly very inadequate. -
Holger over 8 yearsSince this code is using a Generic API, this is what the concept calls a boxed representation which can be
null
. However, since such a hypothetical language change would cause the compiler to issue an error here (not break the code silently), I can live with the fact, that it would possibly have to be adapted for Java 10. I suppose, theStream
API will look quite different then as well… -
Felix over 7 yearsAdd 3rd code snippet, I see
Optional.empty
as a result of.stream().map(Optional::ofNullable).findFirst().orElse(null)
(notnull
) - in case the first element in stream isnull
. -
neverendingqs over 6 yearsI want
firstString
to benull
if the first item instrings
isnull
. -
Mattos over 6 yearsunfortunately it uses
Optional.of
which is not null safe. You couldmap
toOptional.ofNullable
and then usefindFirst
but you will end up with an Optional of Optional -
danny almost 5 yearsI think it makes more sense if
findFirst
returns an empty Optional value in PO's case