使用 Comparator.nullsLast 时出现 NullPointerException
NullPointerException when using Comparator.nullsLast
我在下面的代码中包含 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$comparinga9974f(Comparator.java:469)
at java.util.Comparator.lambda$thenComparing697e65(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)
问题出在下一行(为了清楚起见,我删除了 Comparator.
):
Comparator<MyCustomValue> valueComparator = nullsLast(comparing(MyCustomValue::getValue));
您创建的 Comparator
将处理 null
类型的 MyCustomValue
值。它不会处理 getValue
返回的 null
s。您必须使用 Comparator.comparing
的 2 参数版本并为值提供一个 null
安全比较器:
valueComparator = comparing(MyCustomValue::getValue, nullsLast(naturalOrder()));
以上将处理您实际想要按 value
排序的常见情况。当我查看您的代码时,我认为您的意思是只使用 value
进行 null
检查,否则不想按它排序。如果是这种情况,您可以使用 nullsLast( (x,y) -> 0)
作为 comparing
的 null 安全第二个参数,它将认为所有字符串都相等。您也可以使用 valueComparator = comparing(mcv -> mcv.getValue() == null)
,因为 true
按照自然顺序排在 false
之后,但这可能不太清楚。
如果您还想处理 MyCustomValue
中的 null
个,则必须再次将其包装在 nullsLast
中。
我在下面的代码中包含 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$comparinga9974f(Comparator.java:469)
at java.util.Comparator.lambda$thenComparing697e65(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)
问题出在下一行(为了清楚起见,我删除了 Comparator.
):
Comparator<MyCustomValue> valueComparator = nullsLast(comparing(MyCustomValue::getValue));
您创建的 Comparator
将处理 null
类型的 MyCustomValue
值。它不会处理 getValue
返回的 null
s。您必须使用 Comparator.comparing
的 2 参数版本并为值提供一个 null
安全比较器:
valueComparator = comparing(MyCustomValue::getValue, nullsLast(naturalOrder()));
以上将处理您实际想要按 value
排序的常见情况。当我查看您的代码时,我认为您的意思是只使用 value
进行 null
检查,否则不想按它排序。如果是这种情况,您可以使用 nullsLast( (x,y) -> 0)
作为 comparing
的 null 安全第二个参数,它将认为所有字符串都相等。您也可以使用 valueComparator = comparing(mcv -> mcv.getValue() == null)
,因为 true
按照自然顺序排在 false
之后,但这可能不太清楚。
如果您还想处理 MyCustomValue
中的 null
个,则必须再次将其包装在 nullsLast
中。