Skip to content

Comparator的使用与常用场景详解


一、是什么?

Comparator 是 Java 中的一个函数式接口java.util.Comparator),用于定义对象的自定义排序规则。它不修改原类,而是通过外部比较器实现排序逻辑,特别适用于无法修改类源码或需要多种排序方式的场景。

二、解决什么问题

  1. 对象排序定制化:当对象本身未实现 Comparable 接口,或需要多种排序方式(如按年龄、姓名、分数等不同字段排序)时。
  2. 解耦排序逻辑:将排序规则与业务逻辑分离,避免修改原有类代码。
  3. 函数式编程支持:结合 Lambda 表达式简化代码(JDK 8+ 特性)。

三、核心方法

java
int compare(T o1, T o2) // 核心方法:返回负数、0、正数表示 o1 <、=、> o2

JDK 8+ 新增的便捷方法:

java
Comparator.comparing(Function keyExtractor) // 按指定字段排序
Comparator.thenComparing(Comparator other) // 多级排序
Comparator.reversed() // 反转排序
Comparator.nullsFirst()/nullsLast() // 处理 null 值

四、应用场景

  1. 集合排序:对 List 或数组进行自定义排序。
  2. 算法题高频应用
    • 多维排序(先按分数降序,再按年龄升序)
    • 自定义数据结构排序(如对 List<Point> 按距离原点距离排序)
    • 字符串特殊排序(如按字符串长度、特定字符出现次数)
    • 处理边界值(如 null 值置顶/置底)
  3. Stream API 操作:在 sorted() 中动态指定排序规则。

五、Java 示例

java
// 1. 基础用法:按字符串长度排序
Comparator<String> byLength = (s1, s2) -> s1.length() - s2.length();
List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
list.sort(byLength); // [Apple, Cherry, Banana]

// 2. 多级排序:先按分数降序,再按姓名升序
Comparator<Student> comparator = Comparator
    .comparing(Student::getScore).reversed()
    .thenComparing(Student::getName);
students.sort(comparator);

// 3. 处理 null 值:将 null 放在末尾
Comparator<Integer> nullsLast = Comparator.nullsLast(Integer::compare);
List<Integer> nums = Arrays.asList(3, null, 1, 2);
nums.sort(nullsLast); // [1, 2, 3, null]

六、与 Comparable 的区别

特性ComparatorComparable
实现位置外部比较器(单独类/Lambda)内部比较器(需修改类源码)
排序方式支持多种自定义排序规则仅支持一种自然排序规则
侵入性无侵入,不修改原类需在原类中实现接口
null 处理支持 nullsFirst()/nullsLast()需手动处理 null
适用场景第三方类排序、多规则排序定义类的默认自然顺序

七、重要注意事项

  1. 比较逻辑一致性:必须满足自反性、对称性、传递性(如 a>bb>ca>c)。
  2. 性能优化:避免在 compare() 中创建新对象(如频繁拼接字符串)。
  3. JDK 8+ 推荐:优先使用 Comparator.comparing() 链式调用,而非手动实现 compare
  4. 空指针处理:使用内置的 nullsFirst()/nullsLast() 避免 NPE。
  5. 逆序技巧:调用 reversed() 比手动写负号更易读。

八、总结

Comparator 是 Java 中实现灵活排序的核心工具,尤其适合算法题中需要快速定义多种排序规则的场景。其核心优势在于:

  1. 解耦性强:无需修改原有类即可实现排序。
  2. 函数式支持:Lambda 表达式大幅简化代码(JDK 8+)。
  3. 组合能力:通过 thenComparing() 轻松实现多级排序。

在刷题时的常用套路:

示例代码题高频用法

java
// 力扣常见场景:对二维数组按第2列降序+第1列升序
Arrays.sort(points, Comparator
    .comparingInt((int[] p) -> p[1]).reversed()
    .thenComparingInt(p -> p[0])
);