NullPointerException при использовании Comparator.nullsLast - PullRequest
0 голосов
/ 28 января 2019

У меня есть код ниже с 2 классами MyRange и MyCustomValue -

class MyRange {
    private Long id;
    private Double minValue;
    private Double maxValue;

    // getters and setters
    // equals, hashCode and toString

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null || getClass() != obj.getClass())
            return false;
        MyRange other = (MyRange) obj;
        return Objects.equals(this.id, other.id) && 
               Objects.equals(this.minValue, other.minValue) &&
               Objects.equals(this.maxValue, other.maxValue);
    }
}

class MyCustomValue {
    private String value;
    private MyRange myrange;

    //getters and setters
    // equals, hashCode and toString

}

Если value равно нулю в MyCustomValue, я хочу его последним.Поэтому я пишу компаратор, как показано ниже

public static final Comparator<MyCustomValue> externalMVComparator = (emv1, emv2) -> {
    if(emv1.getValue() != null && emv2.getValue() == null) {
        return -1;
    } else if (emv1.getValue() == null && emv2.getValue() != null) {
        return 1;
    } else {
        return myrangeMinValueComparator.compare(emv1, emv2);
    }
}

private static final Comparator<MyRange> minValueComparator =  Comparator.nullsLast(Comparator.comparingDouble(value -> value.getMinValue()));
private static final Comparator<MyCustomValue> myrangeMinValueComparator = Comparator.nullsLast(Comparator.comparing(MyCustomValue::getMyrange, minValueComparator));

Вышеприведенный компаратор работает нормально.Поэтому я решил изменить externalMVComparator, как показано ниже (то есть, используя thenComparing для большей читабельности)

private static final Comparator<MyCustomValue> valueComparator = Comparator.nullsLast(Comparator.comparing(MyCustomValue::getValue));
public static final Comparator<MyCustomValue> externalMVComparator2 = Comparator.nullsLast(valueComparator.thenComparing(myrangeMinValueComparator));

Но сортировка списка с externalMVComparator2 приводит к NullPointerException.Что это за ошибка в коде?

Код, используемый для тестирования -

MyCustomValue emv1 = new MyCustomValue("v1", new MyRange(1L, 0.71, 0.79));
MyCustomValue emv2 = new MyCustomValue(null, new MyRange(2L, 0.53, 0.65));
MyCustomValue emv3 = new MyCustomValue("v2", new MyRange(3L, 0.28, 0.42));
MyCustomValue emv4 = new MyCustomValue(null, new MyRange(4L, 0.06, 0.27));
List<MyCustomValue> shuffledList1 = Arrays.asList(emv1, emv2, emv3, emv4);
Collections.shuffle(shuffledList1);
shuffledList1.sort(MyCustomValue.externalMVComparator2);
Assert.assertEquals(shuffledList1, Arrays.asList(emv3, emv1, emv4, emv2));

Ошибка трассировки стека -

    Exception in thread "main" java.lang.NullPointerException
    at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)
    at java.util.Comparator.lambda$thenComparing$36697e65$1(Comparator.java:216)
    at java.util.Comparators$NullComparator.compare(Comparators.java:83)
    at java.util.Comparators$NullComparator.compare(Comparators.java:83)
    at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
    at java.util.TimSort.sort(TimSort.java:220)
    at java.util.Arrays.sort(Arrays.java:1438)
    at java.util.Arrays$ArrayList.sort(Arrays.java:3895)
    at TestNullComparator.main(TestNullComparator.java:15)

1 Ответ

0 голосов
/ 29 января 2019

Проблема в следующей строке (для ясности я удалил Comparator.):

Comparator<MyCustomValue> valueComparator = nullsLast(comparing(MyCustomValue::getValue));

Сделанный вами Comparator будет обрабатывать null значения типа MyCustomValue.Он НЕ будет обрабатывать null с, возвращенных getValue.Вы должны использовать версию Comparator.comparing с двумя аргументами и предоставить null -безопасный компаратор для значений:

valueComparator = comparing(MyCustomValue::getValue, nullsLast(naturalOrder()));

Вышеприведенный пример будет обрабатывать общий случай, когда вы действительно хотите отсортировать по value.Когда я смотрю на ваш код, я думаю, что вы хотите использовать только чек value для null, иначе не хотите сортировать по нему.Если это так, вы можете использовать nullsLast( (x,y) -> 0) в качестве нулевого безопасного второго аргумента для comparing, который будет считать все строки равными.Вы также можете использовать valueComparator = comparing(mcv -> mcv.getValue() == null), потому что true идет после false в естественном порядке, но это может быть менее понятно.

Если вы также хотите обработать null s из MyCustomValue, вы бынужно снова завернуть в nullsLast.

...