Java Lambda按对象属性排序触发NullPointerException问题及解决咨询
问题:Java Lambda排序嵌套属性时使用nullsFirst仍触发空指针异常
场景描述
- 按对象的嵌套属性
Person.ageRange.begin排序,部分Person对象的ageRange为null,使用Comparator.nullsFirst()后仍抛出NullPointerException。
示例代码
import java.util.Comparator; import java.util.stream.Collectors; import java.util.List; import java.util.ArrayList; class HelloWorld { public static void main(String[] args) { AgeRange ageRange = new AgeRange(); ageRange.setBegin(1); ageRange.setEnd(18); Person p1 = new Person(); p1.setAgeRange(ageRange); // 修正原代码变量名错误:age -> ageRange Person p2 = new Person(); p2.setAgeRange(null); AgeRange ageRange1 = new AgeRange(); ageRange1.setBegin(19); ageRange1.setEnd(25); Person p3 = new Person(); p3.setAgeRange(ageRange1); List<Person> persons = new ArrayList<>(); persons.add(p1); persons.add(p2); persons.add(p3); persons.stream().sorted(Comparator.nullsFirst(Comparator.comparing(Person::getAgeRange))) .collect(Collectors.toList()); // 此处抛出NullPointerException } } class Person { private String name; private AgeRange ageRange; public String getName(){ return this.name; } public void setName(String name){ this.name = name; } public AgeRange getAgeRange(){ return this.ageRange; } public void setAgeRange(AgeRange ageRange){ this.ageRange = ageRange; } } class AgeRange implements Comparable<AgeRange>{ long begin; long end; public void setBegin(long begin){ this.begin = begin; } public void setEnd(long end){ this.end = end; } public long getBegin(){ return this.begin; } public long getEnd(){ return this.end; } @Override public int compareTo(AgeRange obj){ return Long.compare(begin,obj.begin); } }
异常信息
Exception in thread "main" java.lang.NullPointerException at java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469) at java.base/java.util.Comparators$NullComparator.compare(Comparators.java:83) at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) at java.base/java.util.TimSort.sort(TimSort.java:220) at java.base/java.util.Arrays.sort(Arrays.java:1515) at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:353) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) at HelloWorld.main(HelloWorld.java:34)
问题原因
你当前的写法逻辑有误:
Comparator.nullsFirst()是用来处理被比较的Person对象本身为null的情况,但你的场景是Person对象不为null,只是它的ageRange属性为null。- 当调用
Comparator.comparing(Person::getAgeRange)时,若某个Person的ageRange为null,comparing方法会尝试调用AgeRange的compareTo方法,此时传入的参数是null,直接触发空指针异常。
正确实现方式
方案1:用comparing重载方法处理嵌套属性的null值
直接在comparing中指定处理AgeRange null值的比较器,这是最简洁的写法:
persons.stream() .sorted(Comparator.comparing( Person::getAgeRange, Comparator.nullsFirst(Comparator.comparing(AgeRange::getBegin)) )) .collect(Collectors.toList());
- 第二个参数专门处理
getAgeRange返回的null值:当ageRange为null时,nullsFirst会将其排在前面;非null时则按ageRange.begin排序。
方案2:手动编写lambda比较逻辑
如果需要更灵活的排序规则,可以直接写lambda处理所有情况:
persons.stream() .sorted((p1, p2) -> { if (p1.getAgeRange() == null && p2.getAgeRange() == null) { return 0; } if (p1.getAgeRange() == null) { return -1; // null优先排在前面 } if (p2.getAgeRange() == null) { return 1; } return Long.compare(p1.getAgeRange().getBegin(), p2.getAgeRange().getBegin()); }) .collect(Collectors.toList());
补充:原代码编译错误修正
原代码中p1.setAgeRange(age);是变量名错误,需改为p1.setAgeRange(ageRange);才能正常编译。
内容的提问来源于stack exchange,提问作者Tim
相关产品推荐
相关产品推荐

