Comparable 与 Comparator:对象怎么排序
更新时间:2026-09-20。排序结果常用于集合与 Stream,配合 集合框架 与 Stream 使用。
一、两个接口的分工
Comparable<T> | Comparator<T> | |
|---|---|---|
| 位置 | 实现在类自身上 | 独立外部策略类/lambda |
| 方法 | int compareTo(T o) | int compare(T a, T b) |
| 数量 | 一个类只能有一种「自然顺序」 | 可写任意多个、临时传入 |
| 典型 | String、Integer 等已实现 | 临时按某字段/多字段排序 |
口诀:Comparable 是「我自己知道我怎么排」(内部自然序),Comparator 是「别人告诉我两个谁大」(外部策略)。
二、Comparable.compareTo
public record Employee(long id, String name, int age) implements Comparable<Employee> {
@Override
public int compareTo(Employee o) {
return Long.compare(this.id, o.id); // 自然顺序:按 id 升序
}
}
Collections.sort(list); // 元素必须实现 Comparable,否则 ClassCastException
list.sort(null); // 传 null = 用自然顺序返回值契约:负表示 this 在前,0 表示相等,正表示 this 在后——并不要求恰好是 -1/0/1。
三、Comparator:灵活的外部排序
// 按年龄升序
Comparator<Employee> byAge = (a, b) -> Integer.compare(a.age(), b.age());
list.sort(byAge);
// JDK8+ 链式:name 为 null 的排前面,先按 name,平局再按 age
Comparator<Employee> c =
Comparator.comparing(Employee::name, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparingInt(Employee::age);
list.sort(c);
list.sort(c.reversed()); // 整体反转常用工厂/方法:
Comparator.comparing(Function)/comparingInt/Long/Double(避免装箱);.thenComparing(...)/thenComparingInt(...):多级、平局再比;.reversed():反转;Comparator.naturalOrder()/reverseOrder();.nullsFirst(c)/.nullsLast(c):处理 null,避免 NPE。
四、不要用减法做比较(溢出坑)
// 危险:int 相减可能溢出导致排序错误
Comparator<Integer> bad = (a, b) -> a - b; // a=Integer.MIN_VALUE,b>0 时结果反了
// 正确:
Comparator<Integer> good = Integer::compare; // 或 Comparator.comparingInt(x -> x)整型一律用 Integer.compare/Long.compare 或 comparingInt,不要 a - b。
五、compareTo 与 equals 一致性
若 compareTo 返回 0 的两个对象,建议 equals 也为 true(「与 equals 一致」)。不一致时放进 TreeSet/TreeMap 这类「按比较结果判定相等」的有序集合会出现反直觉行为:TreeSet 用 compareTo==0 当作重复而拒绝添加,哪怕 equals 不等。BigDecimal 的 new BigDecimal("1.0") 与 new BigDecimal("1.00") 就是 equals 不等但 compareTo 为 0 的经典例子。
六、都用在哪
Collections.sort(list); // 自然顺序
list.sort(Comparator.comparing(...)); // 指定比较器
Collections.binarySearch(list, key, cmp);
new TreeSet<>(cmp); // 有序集合/映射按比较器排序去重
stream.sorted(cmp).collect(toList()); // Stream 排序(不改原集合)总结
Comparable 在类内部用 compareTo 定义唯一自然顺序,Comparator 是可插拔的外部策略、能临时定义任意多种排序;比较方法的返回值按「负/零/正」表示先后而非固定 -1/0/1,JDK8 用 comparing + thenComparing + reversed + nullsFirst 链式表达多级与空值规则;整数比较要用 Integer/Long.compare 而不是相减以防溢出;注意 compareTo 为 0 最好与 equals 一致,否则 TreeSet/TreeMap 会按比较结果判重产生反直觉行为,Stream.sorted 与 Collections.sort 都支持传入 Comparator。