Comparator.compare()必须返回int,因排序契约依赖负数/0/正数三值语义;多字段排序应链式调用thenComparing();需抛受检异常或访问非final变量时须用匿名类;Collections.sort()不支持null而Arrays.sort()默认将null排最前。

Comparator.compare() 方法必须返回 int,不能只写 true/false
很多人初学时误以为 compare() 是个布尔判断,直接写 return a > b; —— 这会导致编译错误,因为方法签名要求返回 int。Java 的排序契约依赖三值语义:负数 表示第一个参数小,0 表示相等,正数 表示第一个参数大。
正确写法是用减法或 Integer.compare() 等安全方法:
Comparatorasc = (a, b) -> a - b; // 仅适用于无溢出风险的 int Comparator safeAsc = (a, b) -> Integer.compare(a, b); // 推荐
对 Long、Double 等类型,务必用对应包装类的 compare() 方法,否则 a - b 可能溢出或产生 NaN。
链式排序用 thenComparing(),别手写嵌套 if
多字段排序(如先按年龄升序,年龄相同时按姓名字典序)容易陷入手动判断逻辑,既冗长又易错。直接用 thenComparing() 链式调用更清晰、可读性高,且天然支持 null 处理策略。
立即学习“Java免费学习笔记(深入)”;
-
thenComparing()返回新Comparator,不修改原对象 - 支持方法引用:
thenComparing(Person::getName) - 可指定 null 优先级:
thenComparing(Comparator.nullsLast(String::compareTo))
Comparatorcmp = Comparator.comparing(Person::getAge) .thenComparing(Person::getName, String.CASE_INSENSITIVE_ORDER) .thenComparing(Comparator.nullsLast(Person::getEmail));
lambda 写法 vs 匿名类:什么时候必须用 new Comparator()
绝大多数场景用 lambda 更简洁,但以下情况无法用 lambda,必须显式写 new Comparator():
- 需要在比较逻辑中抛出受检异常(lambda 无法声明 throws)
- 需要访问外部作用域中的非 final / effectively final 变量(lambda 要求变量事实不可变)
- 需复用同一实例多次(lambda 每次都是新对象,无法 == 判断)
例如读取配置文件决定排序方向时,若配置加载可能抛 IOException,就得用匿名类:
ComparatordynamicCmp = new Comparator () { @Override public int compare(String s1, String s2) { try { return loadSortOrderFromConfig() ? s1.compareTo(s2) : s2.compareTo(s1); } catch (IOException e) { throw new RuntimeException(e); } } };
Arrays.sort() 和 Collections.sort() 对 null 的默认行为不同
这是实际开发中容易踩的坑:Collections.sort() 明确要求列表元素不能含 null(否则抛 NullPointerException),而 Arrays.sort() 对引用数组默认允许 null,但会把 null 排在最前面——前提是没传自定义 Comparator。
一旦你传了 Comparator,两者行为就统一了:都依赖该 Comparator 是否能处理 null。所以如果你的 compare() 方法里直接调 a.toString().compareTo(b.toString()),遇到 null 就崩。
稳妥做法:
- 用
Comparator.nullsFirst()或Comparator.nullsLast()包装已有比较器 - 避免在 lambda 中对参数做未判空的操作
- 测试时务必覆盖含
null元素的边界 case
排序规则不是写完就完的事,真正难的是让所有分支路径在 null、NaN、溢出、时区、大小写等边缘情况下仍稳定输出一致结果。









