使用Stream.sorted时若存在null元素且Comparator未处理null会抛NPE;Comparator.nullsFirst()可安全将null视为最小值排最前,避免异常,支持嵌套防NPE(如nullsLast内层处理字段null),是保留null语义的标准方案。

当使用 Stream.sorted(Comparator) 时,如果流中存在 null 元素,而传入的 Comparator 没有显式处理 null,就会在比较过程中触发 NullPointerException(NPE)。Comparator.nullsFirst() 是 Java 8 引入的工具方法,专门用于安全地将 null 视为“最小值”排在最前,从而避免 NPE。
为什么直接用自定义 Comparator 会 NPE?
比如你写:
list.stream()
.sorted((a, b) -> a.getName().compareTo(b.getName()))
.collect(Collectors.toList());
只要 a 或 b 为 null,调用 .getName() 就立刻抛 NPE。原生 lambda 或匿名类不会自动跳过 null —— 它们只负责“比较”,不负责“判空”。
用 nullsFirst 包装非空比较器
把原本可能崩的比较逻辑,交给 nullsFirst 托管,它会在调用前先检查是否为 null:
立即学习“Java免费学习笔记(深入)”;
- 两个都是
null→ 认为相等,不抛异常 - 一个为
null,一个非null→null排前面 - 两个都非
null→ 才真正执行你写的比较逻辑
改写示例:
list.stream()
.sorted(Comparator.nullsFirst(
Comparator.comparing(Person::getName,
Comparator.nullsLast(String::compareTo))
))
.collect(Collectors.toList());
这里嵌套用了 Comparator.nullsLast(String::compareTo),是因为 Person::getName 可能返回 null,所以内层也要防 NPE;外层 nullsFirst 则确保整个 Person 对象为 null 时也能安全排序。
更简洁的写法:用 method reference + nullsFirst
如果你只是按字段排序且该字段类型支持自然排序(如 String, Integer),可以直接组合:
-
Comparator.nullsFirst(Comparator.naturalOrder())→ 用于Integer、String等可自然排序的类型 -
Comparator.nullsFirst(Comparator.comparing(Person::getAge))→ 如果getAge()返回Integer,也安全(因为comparing默认用naturalOrder) - 但注意:
Comparator.comparing(Person::getName)本身不防getName()返回null,所以仍需内层加nullsLast(String::compareTo)
替代方案:提前过滤或映射,但不推荐
有人会想“那我先 .filter(Objects::nonNull) 不就行了?”——可以,但改变了原始语义(丢弃了 null)。如果业务要求 null 必须保留并排在开头或结尾,就必须用 nullsFirst 或 nullsLast。它们是语义完整、零异常、一行解决的标准做法。


















