3

Lets say you have a list of integers containing null vaues

List<Integer> integers = Arrays.asList(null, 9, 11, 7, 5, null);

Now if you want to get max value, you can try

OptionalInt max = integers.stream()
        .mapToInt(Integer::valueOf)
        .max();

This would work if the list did not contain nulls. In our case there will be a NullPointerException. After some research I can see such solution

Optional<Integer> max = integers.stream()
        .max(Comparator.nullsFirst(Comparator.naturalOrder()));

It works if you consider nulls less then non-nulls. But if you want them to be greater and change nullsFirst to nullsLast

Optional<Integer> max = integers.stream()
        .max(Comparator.nullsLast(Comparator.naturalOrder()));

then you will meet NullPointerException again.

I am interesting - why nullsLast doesn't work in this case considering that its doc states that it is null-friendly? And what is the best way to sort list of Integers containing nulls?

Thanks for your ideas!

Bogdan
  • 310
  • 6
  • 12
  • Consider changing the name of this question. It has nothing to do with sorting, has it? – Erk Jul 24 '18 at 19:19

3 Answers3

4

That's not about Comparator per-se:

List<Integer> result = integers.stream()
            .sorted(Comparator.nullsLast(Comparator.naturalOrder()))
            .collect(Collectors.toList());
System.out.println(result); // [5, 7, 9, 11, null, null]

You are putting your nulls to the end and that would be max; calling max on a null throws that NullPointerException.

Eugene
  • 117,005
  • 15
  • 201
  • 306
3

The documentation of Stream#max states:

Throws: NullPointerException - if the maximum element is null

As you provided a comparator that will consider null as a maximum element, then that is why it throws the exception.

This is what the stacktrace indicates when it tries to return Optional.of(state); with state being null in your example, and Optional.of throws a NPE if the value being passed is null.

Comparator.nullsLast works fine, for example:

Integer max = 
    Collections.max(integers, Comparator.nullsLast(Comparator.naturalOrder()));

will indeed give you null.

Alexis C.
  • 91,686
  • 21
  • 171
  • 177
2

If you consider null to be larger than any non-null value, the maximum will be, of course, null when list contains any null.

Since Stream.max already documents that it will throw a NullPointerException when the result is null, this is expected behavior. The design rationale is similar to findFirst(), which has been discussed in this Q&A.

Note that even

Optional<Integer> max = integers.stream()
    .max(Comparator.nullsFirst(Comparator.naturalOrder()));

can still fail, i.e. if all values are null. But there is no point in using a custom Comparator here, as the solution is straight-forward:

OptionalInt max = integers.stream()
    .filter(Objects::nonNull)
    .mapToInt(Integer::valueOf)
    .max();

When you get an empty OptionalInt, you may still test integers.isEmpty() to determine whether all elements were null or there were no elements at all.

Holger
  • 285,553
  • 42
  • 434
  • 765