Comparator.compare()必须返回int,因排序契约依赖负数/0/正数三值语义;多字段排序应链式调用thenComparing();需抛受检异常或访问非final变量时须用匿名类;Collections.sort()不支持null而Arrays.sort()默认将null排最前。
很多人初学时误以为 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() 链式调用更清晰、可读性高,且天然支持 null 处理策略。
thenComparing() 返回新 Comparator,不修改原对象thenComparing(Person::getName)
thenComparing(Comparator.nullsLast(String::compareTo))
Comparatorcmp = Comparator.comparing(Person::getAge) .thenComparing(Person::getName, String.CASE_INSENSITIVE_ORDER) .thenComparing(Comparator.nullsLast(Person::getEmail));
绝大多数场景用 lambda 更简洁,但以下情况无法用 lambda,必须显式写 new Comparator():
例如读取配置文件决定排序方向时,若配置加载可能抛 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); } } };
这是实际开发中容易踩的坑:Collections.sort() 明确要

null(否则抛 NullPointerException),而 Arrays.sort() 对引用数组默认允许 null,但会把 null 排在最前面——前提是没传自定义 Comparator。
一旦你传了 Comparator,两者行为就统一了:都依赖该 Comparator 是否能处理 null。所以如果你的 compare() 方法里直接调 a.toString().compareTo(b.toString()),遇到 null 就崩。
稳妥做法:
Comparator.nullsFirst() 或 Comparator.nullsLast() 包装已有比较器null 元素的边界 case排序规则不是写完就完的事,真正难的是让所有分支路径在 null、NaN、溢出、时区、大小写等边缘情况下仍稳定输出一致结果。