1. 定义
Comparable
- 包:java.lang
- 定义:一个接口,定义在类内部,表示该类对象具有自然排序能力。
- 方法:int compareTo(T o),比较当前对象与参数对象的大小。
- 特点:需要被比较的类本身实现该接口。
Comparator
- 包:java.util
- 定义:一个独立接口,用于定义外部比较逻辑,不要求被比较的类修改。
- 方法:int compare(T o1, T o2),比较两个对象的大小。
- 特点:通常作为外部比较器传递给排序方法或集合。
2. 主要区别
特性 | Comparable | Comparator |
---|---|---|
位置 | 类内部实现 | 外部定义,通常独立类或 Lambda |
方法 | compareTo(T o) | compare(T o1, T o2) |
侵入性 | 修改目标类(侵入式) | 不修改目标类(非侵入式) |
使用场景 | 定义类的自然排序 | 提供灵活的自定义排序 |
实例数量 | 一个类只能有一个自然顺序 | 可为同一类定义多个比较器 |
典型使用 | String、Integer 等内置类的排序 | 集合排序(如 Collections.sort) |
3. 代码示例
Comparable 示例
定义自然排序(如按年龄排序):
class Student implements Comparable<Student> {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student other) {
return Integer.compare(this.age, other.age); // 按年龄升序
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
public class Main {
public static void main(String[] args) {
TreeSet<Student> set = new TreeSet<>();
set.add(new Student("张三", 20));
set.add(new Student("李四", 18));
set.add(new Student("王五", 22));
System.out.println(set); // 按年龄排序
}
}
输出
[Student{name='李四', age=18}, Student{name='张三', age=20}, Student{name='王五', age=22}]
Comparator 示例
外部定义多种排序规则
import java.util.*;
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
public class Main {
public static void main(String[] args) {
// 按年龄排序
Comparator<Student> ageComparator = (s1, s2) -> Integer.compare(s1.getAge(), s2.getAge());
TreeSet<Student> set1 = new TreeSet<>(ageComparator);
set1.add(new Student("张三", 20));
set1.add(new Student("李四", 18));
set1.add(new Student("王五", 22));
System.out.println("按年龄排序: " + set1);
// 按姓名排序
Comparator<Student> nameComparator = Comparator.comparing(Student::getName);
TreeSet<Student> set2 = new TreeSet<>(nameComparator);
set2.add(new Student("张三", 20));
set2.add(new Student("李四", 18));
set2.add(new Student("王五", 22));
System.out.println("按姓名排序: " + set2);
}
}
输出
按年龄排序: [Student{name='李四', age=18}, Student{name='张三', age=20}, Student{name='王五', age=22}]
按姓名排序: [Student{name='张三', age=20}, Student{name='李四', age=18}, Student{name='王五', age=22}]
4. 使用场景
Comparable
- 适用:
- 类有明确的自然排序(如 String 按字典序,Integer 按数值)。
- 你控制类的代码,可以修改它。
- 示例:
- TreeSet、TreeMap 默认使用元素的 compareTo。
- 内置类如 String、Integer 已实现。
Comparator
- 适用:
- 不想修改原始类(无源码或不便修改)。
- 需要多种排序方式。
- 临时排序需求。
- 示例:
- Collections.sort(list, comparator)。
- Arrays.sort(array, comparator)。
- TreeSet 或 TreeMap 的构造方法传递比较器。
5. 实现细节
Comparable
- 返回值:
- < 0:当前对象小于参数对象。
- = 0:相等。
- > 0:当前对象大于参数对象。
- 要求:
- 自反性、对称性、传递性。
- 与 equals 一致(建议但非强制)。
Comparator
- 返回值:同上。
- 便捷方法(Java 8+):
- Comparator.comparing(keyExtractor):按指定字段排序。
- thenComparing():多条件排序。
- reversed():反转顺序。
示例(多条件排序)
Comparator<Student> comparator = Comparator
.comparing(Student::getAge) // 按年龄排序
.thenComparing(Student::getName); // 年龄相同按姓名排序
6. 优缺点
Comparable
- 优点:
- 定义自然顺序,简单直接。
- 默认支持排序集合。
- 缺点:
- 侵入式,只能有一种排序方式。
- 不灵活。
Comparator
- 优点:
- 非侵入式,类无需改动。
- 支持多种排序规则。
- 缺点:
- 需要额外定义,代码稍复杂。
- 不作为类的默认行为。
7. 总结
- Comparable:
- 类内部实现,定义自然排序。
- 适合固定排序规则。
- Comparator:
- 外部定义,灵活指定排序。
- 适合动态或多样的排序需求。
- 选择依据:
- 如果类有唯一自然的顺序,用 Comparable。
- 如果需要外部控制或多种排序,用 Comparator。