Comparator的使用与常用场景详解
一、是什么?
Comparator
是 Java 中的一个函数式接口(java.util.Comparator
),用于定义对象的自定义排序规则。它不修改原类,而是通过外部比较器实现排序逻辑,特别适用于无法修改类源码或需要多种排序方式的场景。
二、解决什么问题
- 对象排序定制化:当对象本身未实现
Comparable
接口,或需要多种排序方式(如按年龄、姓名、分数等不同字段排序)时。 - 解耦排序逻辑:将排序规则与业务逻辑分离,避免修改原有类代码。
- 函数式编程支持:结合 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 值
四、应用场景
- 集合排序:对
List
或数组进行自定义排序。 - 算法题高频应用:
- 多维排序(先按分数降序,再按年龄升序)
- 自定义数据结构排序(如对
List<Point>
按距离原点距离排序) - 字符串特殊排序(如按字符串长度、特定字符出现次数)
- 处理边界值(如 null 值置顶/置底)
- 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 的区别
特性 | Comparator | Comparable |
---|---|---|
实现位置 | 外部比较器(单独类/Lambda) | 内部比较器(需修改类源码) |
排序方式 | 支持多种自定义排序规则 | 仅支持一种自然排序规则 |
侵入性 | 无侵入,不修改原类 | 需在原类中实现接口 |
null 处理 | 支持 nullsFirst() /nullsLast() | 需手动处理 null |
适用场景 | 第三方类排序、多规则排序 | 定义类的默认自然顺序 |
七、重要注意事项
- 比较逻辑一致性:必须满足自反性、对称性、传递性(如
a>b
且b>c
则a>c
)。 - 性能优化:避免在
compare()
中创建新对象(如频繁拼接字符串)。 - JDK 8+ 推荐:优先使用
Comparator.comparing()
链式调用,而非手动实现compare
。 - 空指针处理:使用内置的
nullsFirst()
/nullsLast()
避免 NPE。 - 逆序技巧:调用
reversed()
比手动写负号更易读。
八、总结
Comparator
是 Java 中实现灵活排序的核心工具,尤其适合算法题中需要快速定义多种排序规则的场景。其核心优势在于:
- 解耦性强:无需修改原有类即可实现排序。
- 函数式支持:Lambda 表达式大幅简化代码(JDK 8+)。
- 组合能力:通过
thenComparing()
轻松实现多级排序。
在刷题时的常用套路:
示例代码题高频用法:
java
// 力扣常见场景:对二维数组按第2列降序+第1列升序
Arrays.sort(points, Comparator
.comparingInt((int[] p) -> p[1]).reversed()
.thenComparingInt(p -> p[0])
);