8

I have following code:

List<LocalDate> dates = Arrays.asList(null, null, 
    LocalDate.now(), LocalDate.now().minusDays(9));

LocalDate max = dates.stream()
    .max(Comparator.nullsLast(Comparator.naturalOrder())).get();

Which produce null pointer exception when I try to get the max, but when trying to get min it works ok. It seems unclear cause max takes Comparator which handles null values. How to sort such array with stream preserving null values.

By the way, I checked JavaDoc of max, which states:

@throws NullPointerException if the maximum element is null

EDIT:

LocalDate max = dates.stream()
    .max(Comparator.nullsLast(Comparator.naturalOrder())).orElse(null);

Throws null pointer as well.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
Bartek
  • 2,109
  • 6
  • 29
  • 40

2 Answers2

10

If you expect the answer to be null because it contains one you can do your own reduction.

List<LocalDate> dates = Arrays.asList(null, null,
        LocalDate.now(), LocalDate.now().minusDays(9));

LocalDate max = dates.stream()
        .reduce(LocalDate.MIN,
                BinaryOperator.maxBy(Comparator.nullsLast(Comparator.naturalOrder())));
System.out.println(max);

prints

null

NOTE: This is different in the sense that an empty list returns LocalDate.MIN.

A less obvious option is to replace null with LocalDate.MAX assuming this is never used.

LocalDate max = dates.stream()
        .map(d -> d == null ? LocalDate.MAX : d)
        .max(Comparator.naturalOrder())
        .map(d -> LocalDate.MAX.equals(d) ? null : d)
        .orElse(null);

This returns null for an empty list.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 3
    very nice! was not aware of `LocalDate.MIN`, I wish I knew about it before - could have won a couple or arguments :) – Eugene Aug 07 '18 at 12:13
  • 2
    @Eugene although MIN is a large negative date, anything before about the 1750s is likely to be fraught. e.g. there was no 0 in the Roman number and no year 0 in the Julian calendar. – Peter Lawrey Aug 07 '18 at 14:59
  • I understand that- I looked at the implementation already, thank you anyway. It's good that I don't deal with such dates (like 99% of people), but when you need to reduce with an identity then definitely something like this needed – Eugene Aug 07 '18 at 15:01
3

Having null values in the List and looking for the max value? Firstly get rid of the null values using Stream::filter. Then the job is easy:

LocalDate max = dates.stream()
                     .filter(Objects::nonNull)
                     .max(Comparator.naturalOrder()).get();

If you insist on using the Comparator only, use Comparator::nullsFirst instead:

LocalDate max = dates.stream().max(Comparator.nullsFirst(Comparator.naturalOrder())).get();
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • Maybe having nulls in list and looking for max is not the best idea, but provided comparator defines how to treat null values. – Bartek Aug 07 '18 at 11:49
  • I realized after writing my initial answer that max will still fill with `nullsFirst` when the list contains only nulls. `max` does not allow null as a value. – Malte Hartwig Aug 07 '18 at 11:49