为何使用相同Comparator时Stream.sorted遇null不抛NPE而List.sort会?
为何
list.stream().sorted(comp)与list.sort(comp)处理含null属性对象时表现不同? 代码示例
public class ToSort { private String sortBy; public ToSort(String sortBy) { this.sortBy = sortBy; } public String getSortBy() { return sortBy; } public static void main(String[] args) { Comparator<ToSort> comp = Comparator.comparing(ToSort::getSortBy); ToSort o1 = new ToSort("TEST"); ToSort o2 = new ToSort(null); List list = new ArrayList<>(); list.add(o1); list.add(o2); list.stream().sorted(comp); // ok list.sort(comp); // NullPointerException } }
运行输出
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.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.ArrayList.sort(ArrayList.java:1750) at com.oxand.simeox.model.pojo.ToSort.main(ToSort.java:28)
差异原因分析
核心差异来自两个方法的执行时机和底层实现逻辑:
list.stream().sorted(comp)未触发实际排序:
Stream是惰性求值的,sorted()方法仅创建一个标记了“待排序”状态的Stream对象,不会立即执行排序操作。只有当调用终止操作(比如collect()、forEach()、toArray()等)时,才会真正执行排序逻辑。你代码里仅调用了sorted(),没有后续终止操作,排序根本没执行,自然不会触发空指针异常。
补充:如果给Stream加上终止操作,比如list.stream().sorted(comp).collect(Collectors.toList()),同样会抛出NPE,此时会执行实际排序,比较逻辑和list.sort()完全一致。list.sort(comp)立即执行排序并触发NPE:
ArrayList的sort()方法底层依赖Arrays.sort,而Arrays.sort使用TimSort算法。TimSort在排序前会先执行countRunAndMakeAscending方法,该方法会遍历元素、尝试构建连续有序的序列,过程中会调用Comparator比较相邻元素。Comparator.comparing(ToSort::getSortBy)生成的比较器,会尝试让null属性与非null属性调用compareTo方法,而null无法调用方法,必然抛出NullPointerException。
内容的提问来源于stack exchange,提问作者Jesus Zavarce
相关产品推荐
相关产品推荐

