I am new to functional programming in java, and wonder how I should code to avoid NPE in (for example) this operation:
myList.stream()
.reduce((prev, curr) -> prev.getTimestamp().isAfter(curr.getTimestamp()) ? prev : curr);
.get().getTimestamp();
My intent here is to find the timestamp of the newest object in the list. Suggestions on how to better collect the last element are very welcome, but my main question here is actually why this works.
The documentation says that the function throws a NullPointerException
"if the result of the reduction is null":
http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-java.util.function.BinaryOperator-
That is OK, but what I don't quite understand is why I don't get a NullPointerException
when this code is run with a list containing only one element. I expected prev
to be null in such a case. I tried debugging, but it just seems to step over the entire lambda expression when there is only one element.
As the JavaDoc of reduce says, reduce is equivalent to :
boolean foundAny = false;
T result = null;
for (T element : this stream) {
if (!foundAny) {
foundAny = true;
result = element;
}
else
result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();
Therefore, if the Stream has a single element, there is only one iteration of the loop, and the single element found in that iteration is returned.
The BinaryOperator
would only be applied if the Stream
has at least two elements.
Better:
Optional<Thingie> max =
myList.stream()
.reduce(BinaryOperator.maxBy(comparing(Thingie::getTimeStamp));
This overload of reduce()
returns an Optional; blindly unpacking it with get
is dangerous, and risks throwing NSEE. Unpack it with one of the safe operators like orElse
or orElseThrow
.
If there's a chance there are nulls in your stream, filter it first with .filter(t -> t != null)
.
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