Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working of Comparator.nullsFirst when both are null

I have a class with several "optional" (not java.util.Optional) fields. I was writing a Lambda comparator to test for equality by comparing a subset of their attributes

I wrote

private final static Comparator<MyEntity> COMPARATOR_491 =  
            comparing(MyEntity::getIsin)
             .thenComparing(MyEntity::getTMarketType)
             .thenComparing(nullsFirst(comparing(MyEntity::getIsoCode)))
             .thenComparing(MyEntity::getTaxRate)
             .thenComparing(nullsFirst(comparing(MyEntity::getEndDate)));
  • ISIN is not null
  • Market type is not null
  • Is code can be null
  • Tax rate is not null
  • End date can be null

The problem is that often I get a NullPointerException. This is the (barely readable) stack trace

java.lang.NullPointerException: null
    at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469) ~[?:1.8.0_51]
    at java.util.Comparator$$Lambda$40/221702765.compare(Unknown Source) ~[?:?]
    at java.util.Comparators$NullComparator.compare(Comparators.java:83) ~[?:1.8.0_51]
    at java.util.Comparator.lambda$thenComparing$36697e65$1(Comparator.java:217) ~[?:1.8.0_51]
    at java.util.Comparator$$Lambda$42/770739971.compare(Unknown Source) ~[?:?]
    at java.util.Comparator.lambda$thenComparing$36697e65$1(Comparator.java:216) ~[?:1.8.0_51]
    at java.util.Comparator$$Lambda$42/770739971.compare(Unknown Source) ~[?:?]
    at java.util.Comparator.lambda$thenComparing$36697e65$1(Comparator.java:216) ~[?:1.8.0_51]
    at java.util.Comparator$$Lambda$42/770739971.compare(Unknown Source) ~[?:?]

I found that two sample entities differ by the End date. The first entity has non-null property, the second has null

I thought that nullsFirst comparer could be null safe when one or both arguments are null.

What am I doing wrong?

like image 342
usr-local-ΕΨΗΕΛΩΝ Avatar asked May 24 '17 07:05

usr-local-ΕΨΗΕΛΩΝ


1 Answers

The comparator returned by nullsFirst(…) returns a comparator which handles the case that one or both of the objects to compare are null.

So when you say nullsFirst(comparing(MyEntity::getIsoCode)), you get a comparator handling the case that either or both MyEntity instances are null and comparing the getIsoCode property according to their natural order (not handling null values), if neither MyEntity instance is null.

What you want achieve, is comparing(MyEntity::getIsoCode, nullsFirst(naturalOrder())), specify the null-safe comparator to be used to compare the property values. The same applies to the getEndDate property.

You may fuse it with thenComparing, to previousComparator.thenComparing(MyEntity::getIsoCode, nullsFirst(naturalOrder()))

like image 112
Holger Avatar answered Nov 15 '22 17:11

Holger