Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling stream().reduce() on a list with only one element

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.

like image 514
aweibell Avatar asked Nov 25 '14 10:11

aweibell


2 Answers

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.

like image 130
Eran Avatar answered Oct 02 '22 19:10

Eran


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

like image 42
Brian Goetz Avatar answered Oct 02 '22 18:10

Brian Goetz