Comparator 比较器
Comparator 是一个函数式接口,用于定义对象的比较规则。它允许在不修改原始类的情况下,定义多种不同的比较方式。
java
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
// 还有其他默认方法和静态方法
}基本使用示例
1 创建 Comparator
java
// 传统方式:实现类
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
}
// Lambda 表达式(Java 8+)
Comparator<Person> nameComparator = (p1, p2) ->
p1.getName().compareTo(p2.getName());
// 方法引用
Comparator<Person> nameComparator =
Comparator.comparing(Person::getName);2 使用示例
java
import java.util.*;
class Person {
private String name;
private int age;
public Person(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 name + "(" + age + ")";
}
}
public class ComparatorExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Alice", 20)
);
// 1. 按姓名排序
people.sort(Comparator.comparing(Person::getName));
System.out.println("按姓名排序: " + people);
// 2. 按年龄排序
people.sort(Comparator.comparingInt(Person::getAge));
System.out.println("按年龄排序: " + people);
// 3. 先按姓名,再按年龄排序
people.sort(Comparator
.comparing(Person::getName)
.thenComparingInt(Person::getAge));
System.out.println("先姓名后年龄: " + people);
}
}Comparator 的常用方法
1 工厂方法(Java 8+)
java
// 自然排序(需要对象实现 Comparable)
Comparator.naturalOrder()
Comparator.reverseOrder()
// 根据对象的属性创建比较器
Comparator.comparing(Function<? super T, ? extends U> keyExtractor)
Comparator.comparingInt(ToIntFunction<? super T> keyExtractor)
Comparator.comparingDouble(ToDoubleFunction<? super T> keyExtractor)
Comparator.comparingLong(ToLongFunction<? super T> keyExtractor)
// null 值处理
Comparator.nullsFirst(Comparator<? super T> comparator)
Comparator.nullsLast(Comparator<? super T> comparator)2 链式调用
java
List<Person> people = new ArrayList<>();
// 多级排序
people.sort(
Comparator.comparing(Person::getLastName)
.thenComparing(Person::getFirstName)
.thenComparingInt(Person::getAge)
.reversed() // 反转排序顺序
);
// 处理 null 值
people.sort(
Comparator.comparing(Person::getName,
Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER))
);完整案例
java
import java.util.*;
import java.util.Comparator;
class Student {
private String name;
private int score;
private String major;
public Student(String name, int score, String major) {
this.name = name;
this.score = score;
this.major = major;
}
// getters
public String getName() { return name; }
public int getScore() { return score; }
public String getMajor() { return major; }
@Override
public String toString() {
return name + "[" + major + "]" + ":" + score;
}
}
public class AdvancedComparatorExample {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 85, "CS"),
new Student("Bob", 92, "Math"),
new Student("Charlie", 78, "CS"),
new Student("Alice", 90, "Math"),
new Student(null, 88, "Physics")
);
System.out.println("原始列表: " + students);
// 1. 按专业排序,专业相同按分数降序
Comparator<Student> byMajorThenScore = Comparator
.comparing(Student::getMajor)
.thenComparing(Comparator
.comparingInt(Student::getScore)
.reversed());
students.sort(byMajorThenScore);
System.out.println("\n按专业,分数降序: " + students);
// 2. 按姓名排序(处理 null 值)
Comparator<Student> byNameWithNulls = Comparator
.comparing(Student::getName,
Comparator.nullsFirst(String::compareTo));
students.sort(byNameWithNulls);
System.out.println("\n按姓名(null 在前): " + students);
// 3. 自定义复杂比较器
Comparator<Student> customComparator = (s1, s2) -> {
// 首先按专业长度排序
int majorLengthCompare =
Integer.compare(s1.getMajor().length(),
s2.getMajor().length());
if (majorLengthCompare != 0) {
return majorLengthCompare;
}
// 然后按分数是否大于 80 分组
boolean s1HighScore = s1.getScore() > 80;
boolean s2HighScore = s2.getScore() > 80;
if (s1HighScore != s2HighScore) {
return s1HighScore ? -1 : 1; // 高分在前
}
// 最后按姓名
return s1.getName().compareTo(s2.getName());
};
students.sort(customComparator);
System.out.println("\n自定义复杂排序: " + students);
}
}实际应用场景
java
// 1. 集合排序
Collections.sort(list, comparator);
list.sort(comparator);
// 2. 数组排序
Arrays.sort(array, comparator);
// 3. 流操作
list.stream()
.sorted(comparator)
.collect(Collectors.toList());
// 4. TreeSet/TreeMap
TreeSet<Person> set = new TreeSet<>(comparator);
TreeMap<Person, String> map = new TreeMap<>(comparator);
// 5. PriorityQueue
PriorityQueue<Person> queue =
new PriorityQueue<>(comparator);Comparator vs Comparable
特性对比
| 特性 | Comparable | Comparator |
|---|---|---|
| 定义位置 | 在类的内部实现 | 独立的类或匿名类 |
| 排序方式 | 自然排序(一种) | 多种排序方式 |
| 方法 | compareTo(T o) | compare(T o1, T o2) |
| 包 | java.lang | java.util |
| 使用场景 | 类有自然排序时 | 需要多种排序方式时 |
选择方式
| 考虑因素 | 推荐选择 | 理由 |
|---|---|---|
| 需要多种排序方式 | Comparator | 灵活,可以定义多个比较器 |
| 类有明显自然顺序 | Comparable | 符合直觉(如日期、数字) |
| 第三方类排序 | Comparator | 无法修改源代码 |
| 作为库的API设计 | Comparable | 方便用户直接使用 |
| 业务对象排序 | Comparator | 业务规则可能变化 |
| 值对象/核心实体 | Comparable | 有明确的标识顺序 |
| 需要链式/组合排序 | Comparator | 支持 thenComparing |
总结
简单来说
- 95% 的情况应该用 Comparator - 更灵活,更容易维护
- 5% 的特殊情况用 Comparable - 当类真的有"自然顺序"时
现代 Java 开发的最佳实践
- 默认不实现 Comparable,除非有充分的理由
- 使用 Comparator 的工厂方法(Comparator.comparing() 等)
- 将常用的 Comparator 定义为常量或静态工厂方法
- 考虑使用 Comparator 的组合功能进行多级排序
朔风