中 基础
Comparable与Comparator#
一句话答案#
Comparable是”内部比较器”,类自身实现compareTo定义自然排序;Comparator是”外部比较器”,独立定义compare,可不改源码、灵活定制多种排序规则。
核心要点
1. Comparable(java.lang)— 自然排序
class User implements Comparable<User> {
int age;
public int compareTo(User o) { return this.age - o.age; } // 定义"天生"的排序
}
Collections.sort(users); // 直接按自然顺序排java- 位于
java.lang,单方法compareTo(T o)。 String、Integer、包装类等都已实现,所以能直接排序。- TreeMap/TreeSet 默认依赖元素的 Comparable。
2. Comparator(java.util)— 定制排序
users.sort(Comparator.comparingInt(u -> u.age)); // 按年龄
users.sort(Comparator.comparing(User::getName).reversed()); // 按名字倒序
users.sort(Comparator.comparingInt(User::getAge)
.thenComparing(User::getName)); // 多级排序java- 位于
java.util,核心方法compare(T o1, T o2)。 - 不修改原类,可定义任意多套规则;是函数式接口,可用 Lambda。
3. 返回值约定(两者一致)
- 负数:第一个参数排在前
- 0:相等(TreeSet/TreeMap 据此判定”重复”,与 equals 无关!)
- 正数:第一个参数排在后
4. 何时用哪个
| 场景 | 选择 |
|---|---|
| 类有唯一、天然的排序方式且能改源码 | Comparable |
| 排序规则多变、不能改源码、需多套规则 | Comparator |
| TreeMap/TreeSet 自定义排序 | 构造时传 Comparator(优先于元素的 Comparable) |
面试回答(2分钟版)
Comparable 和 Comparator 都用于定义对象排序,区别在于谁来定义规则。Comparable 在 java.lang 包,是内部比较器,需要类自己实现 compareTo 方法,定义这个类天生的自然排序顺序,比如 String、Integer 都实现了 Comparable,所以能直接 sort。缺点是排序规则写死在类里,只能有一种,而且要改源码。Comparator 在 java.util 包,是外部比较器,独立实现 compare 方法,最大的好处是不用改原类、可以定义任意多种排序规则,而且它是函数式接口,可以用 Lambda 和 comparing、thenComparing 这些方法链式组合多级排序,非常灵活。两者的返回值约定一致:负数表示第一个排前面,0 表示相等,正数表示排后面。实际开发中,如果一个类有唯一明确的自然顺序就实现 Comparable,如果排序规则多变或者不能改源码就用 Comparator。还有个坑点:TreeSet 和 TreeMap 判断元素是否重复是依据比较器返回 0,而不是 equals,所以如果 compareTo 写得不严谨,可能出现 equals 不相等但被当成重复元素去重的情况。
追问与易错
追问方向:
- “TreeSet 去重靠 equals 还是 compareTo?”→ 靠 compareTo/compare 返回 0,与 equals 无关。比较器不一致会导致逻辑上不相等的元素被去重
- “compareTo 用 a-b 有什么风险?”→ 整数相减可能溢出(极大正数减极小负数),应优先用
Integer.compare(a,b) - “Comparator 怎么实现多字段排序?”→
comparing(...).thenComparing(...)链式组合,或reversed()反转 - “一个类同时实现 Comparable 又传了 Comparator,用哪个?”→ 显式传入的 Comparator 优先于元素自身的 Comparable
- “compareTo 要和 equals 保持一致吗?”→ 强烈建议一致,否则在 TreeMap/TreeSet 等基于比较的容器中行为会和 HashMap 不一致
易错点:
- ❌ “compareTo 直接写 a-b”——有溢出风险,用
Integer.compare/Long.compare - ❌ “TreeSet 去重靠 equals”——靠比较结果是否为 0
- ❌ 混淆包:Comparable 在 java.lang,Comparator 在 java.util