Java 里用 Comparable 给对象排序,记住 compareTo 返回值规则就行
实现 Comparable 接口并重写 compareTo,就能用 Arrays.sort 对自定义对象排序,顺便聊聊几个容易踩的坑。
支持通配符SSL证书、多域名证书、IP证书。适配ACME接口, 支持Zerossl、Let's Encrypt和Google等渠道。登录已有账号
2026-06-28 12:02:11 Java Stream Comparator NullPointerException IDEA调试
线上一个排序逻辑突然炸了,堆栈指着 Comparator.comparingInt 那一行,一看集合里有几个对象的排序字段是 null。
这种问题本地跑的时候不一定能马上发现,数据一多,null 值混进来,直接就 NPE。
原因很简单,Comparator.comparingInt 这类方法内部会调用键提取函数,如果函数返回 null,它尝试拆箱成 int 的时候就挂了。
同样地,用 comparing 然后链式调用 thenComparing 之类,哪个键提取返回了 null 都会炸。
比较稳妥的处理方式是用 Comparator.nullsLast 或 nullsFirst 包一层。比如:
result.sort(Comparator.comparing(
StatisticsVO::getFxjgmc,
Comparator.nullsLast(Comparator.comparingInt(fxjgmc -> FIXED_ORDER.indexOf(fxjgmc)))
));
这样 null 值会被统一放到列表末尾,不会再触发空指针。想要 null 排最前面就换成 nullsFirst。
如果不想用 nullsLast 那一套,也可以自己手动在键提取函数里做判断。比如我希望 null 值的排序权重最低,就返回 Integer.MAX_VALUE:
result.sort(Comparator.comparingInt(p -> {
if (p.getFxjgmc() == null) {
return Integer.MAX_VALUE;
}
return FIXED_ORDER.indexOf(p.getFxjgmc());
}));
其实就是让 null 映射到一个固定的远端位置,避免 compare 阶段碰到 null。
不过这种硬编码一个很大值的方式,如果业务里本来就有这种极值,可能会有冲突,但用作“排最后”的场景一般没问题。
Stream 操作里如果集合本身包含 null 元素,也可能在后续处理时出问题,最简单的办法就是先 filter 一遍:
List<String> filteredList = list.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
别小看这一行,有时候集合是从外部接口或者数据库查出来的,到底有没有 null 自己很难完全控制,提前过滤能省很多事。
排序搞定了,调试的时候又碰到一个让人头疼的事:IDEA 里看集合变量,明明 size 显示有元素,但视图里 null 的位置直接不显示,仿佛不存在一样。这时候很容易误以为集合里没有 null,结果 NPE 照样出。
IDEA 的 Data Views 有个选项会隐藏 null 元素。可以到 Settings -> Build, Execution, Deployment -> Debugger -> Data Views -> Java,把 “Hide null elements in Collections” 的勾去掉。
如果用的是新版 IDEA,选项可能叫法有变化,留意类似的意思就行。还有一个 “Enable alternative view for Collections classes” 的选项,那个勾上之后会以更概括的方式展示集合内容,有时候也会让 null 的显示变得奇怪,调试排序问题时建议先关掉。
要是改完设置还是不显示,或者懒得去翻设置,最简单的就是打一行日志:
System.out.println("List content: " + result);
直接把整个集合打印出来,null 会变成字符串 "null" 显示在控制台,有没有一目了然。这种土办法在排查数据问题的时候其实最直接。
最后把排序和 null 处理揉在一起的写法,大概就是下面这样,既控制了 null 的顺序,又不会因为 null 导致排序中断:
result.sort(Comparator.comparing(
StatisticsVO::getFxjgmc,
Comparator.nullsLast(Comparator.comparingInt(fxjgmc -> {
if (fxjgmc == null) {
return Integer.MAX_VALUE;
}
return FIXED_ORDER.indexOf(fxjgmc);
}))
));
这里其实做了两层防护:外层 nullsLast 先把 null 从比较流程里摘出去,内层的键提取函数对值再做一个兜底,即使链条里某个环节逻辑有疏漏也不至于直接炸。
加上前面的调试设置,后面再碰到类似的排序需求,基本就不用半夜爬起来看报警了。
上一篇: 证书有效期缩至90天,手动运维的终局已到
下一篇: Java Stream 排序与 Comparator 的空指针处理,以及 IDEA 调试里 null 不显示的坑
实现 Comparable 接口并重写 compareTo,就能用 Arrays.sort 对自定义对象排序,顺便聊聊几个容易踩的坑。
线上遇到“Channel was garbage collected without being shut down”的错误,梳理了ManagedChannel关闭机制和常见踩坑点,给出实际可用的修复方式。
还在为Java程序阻塞而烦恼?本文将带你轻松掌握CompletableFuture.runAsync,告别传统阻塞模式。通过直观的错误与正确代码对比,你将学会如何用简单的lambda表达式在新线程中运行无返回值任务。快速理解Thread.currentThread().getName()的验证机制,立即解锁高性能并发编程,让你的应用响应更快!
想象一下从精确到极致的BigDecimal世界踏入“四舍五入”的Double领域。本文揭示Java数值转换的奥秘,解析精度损失的陷阱与范围限制的边界,助你明智选择,避免程序中的“财务黑洞”。