Skip to content
章节导航

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

特性对比

特性ComparableComparator
定义位置在类的内部实现独立的类或匿名类
排序方式自然排序(一种)多种排序方式
方法compareTo(T o)compare(T o1, T o2)
java.langjava.util
使用场景类有自然排序时需要多种排序方式时

选择方式

考虑因素推荐选择理由
需要多种排序方式Comparator灵活,可以定义多个比较器
类有明显自然顺序Comparable符合直觉(如日期、数字)
第三方类排序Comparator无法修改源代码
作为库的API设计Comparable方便用户直接使用
业务对象排序Comparator业务规则可能变化
值对象/核心实体Comparable有明确的标识顺序
需要链式/组合排序Comparator支持 thenComparing

总结

简单来说

  • 95% 的情况应该用 Comparator - 更灵活,更容易维护
  • 5% 的特殊情况用 Comparable - 当类真的有"自然顺序"时

现代 Java 开发的最佳实践

  • 默认不实现 Comparable,除非有充分的理由
  • 使用 Comparator 的工厂方法(Comparator.comparing() 等)
  • 将常用的 Comparator 定义为常量或静态工厂方法
  • 考虑使用 Comparator 的组合功能进行多级排序