面试知识库
基础

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)
  • StringInteger、包装类等都已实现,所以能直接排序。
  • 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