Menu
CoddyTech

Comparator 接口

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 45 课,共 87 课。

虽然 Comparable 定义了类的自然顺序,但有时你需要用不同的方式对对象进行排序。Comparator 接口可以让你创建独立的比较逻辑,而无需修改原来的类。

Comparator 是一个外部对象,用于比较两个相同类型的对象。当你想要多种排序方式,或者无法修改类本身时,这会很有用:

import java.util.Comparator;

class NameComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
}

class AgeComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getAge() - p2.getAge();
    }
}

然后,你可以将比较器传递给排序方法:

List<Person> people = new ArrayList<>();
people.add(new Person("Charlie", 30));
people.add(new Person("Alice", 25));

Collections.sort(people, new NameComparator());  // 按姓名排序
Collections.sort(people, new AgeComparator());   // 按年龄排序

为了让代码更简洁,你可以使用 lambda 表达式,因为 Comparator 是一个函数式接口:

Collections.sort(people, (p1, p2) -> p1.getName().compareTo(p2.getName()));

关键区别:Comparable由被比较的类实现,用于定义一种自然顺序;而Comparator是一个独立的类,可以定义无限种自定义顺序。

challenge icon

挑战

简单

让我们构建一个员工排序系统,展示 Comparator 接口的强大之处。你将创建多个比较器,以不同方式按薪资和姓名对员工进行排序,而无需修改 Employee 类本身。

你将把代码分成 4 个文件:

  • Employee.java:创建一个表示员工的类,其中包含 3 个私有字段:name(String)、department(String)和 salary(double)。添加一个用于初始化所有字段的构造函数,以及每个字段对应的 getter 方法。重写 toString(),使其返回:[name] ([department]) - $[salary]
  • SalaryComparator.java:创建一个比较器,按薪资降序排列员工(薪资最高的排在最前)。实现 Comparator<Employee> 接口及其 compare() 方法。使用 Double.compare() 安全地比较薪资值。
  • NameComparator.java:再创建一个比较器,按姓名字母顺序升序排列员工(从 A 到 Z)。实现 Comparator<Employee>,并使用 String 的 compareTo() 方法进行比较。
  • Main.java:创建员工列表,并使用你的比较器对员工进行排序,将所有内容整合起来。你将收到 3 名员工的输入,每名员工占一行,格式为:name,department,salary

    创建一个员工 ArrayList,然后演示这两种排序方式:

    1. 首先,使用你的 SalaryComparator 按薪资排序,并打印每名员工
    2. 打印一个空行
    3. 然后,使用你的 NameComparator 按姓名排序,并打印每名员工

你将收到 3 行输入,每行包含一名员工的数据,格式为:name,department,salary

例如,输入行可能如下所示:Alice,Engineering,75000.0

记得在比较器文件中导入 java.util.Comparator,并在 Main 文件中导入 java.util.ArrayList、java.util.Collections 和 java.util.Scanner。注意,比较器让你可以在外部定义多种排序策略:Employee 类本身不需要了解如何对它进行排序!

亲手试试

import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 从输入中读取 3 名员工的信息
        // 每行的格式:name,department,salary
        String line1 = scanner.nextLine();
        String line2 = scanner.nextLine();
        String line3 = scanner.nextLine();
        
        // TODO: 解析每一行并创建 Employee 对象
        // 提示:使用 split(",") 分隔各个值
        
        // TODO: 创建一个存放员工的 ArrayList,并将这 3 名员工全部加入其中
        
        // TODO: 使用 SalaryComparator 按薪资排序,并打印每名员工的信息
        // 提示:使用 Collections.sort(list, comparator)
        
        // TODO: 打印一个空行
        
        // TODO: 使用 NameComparator 按姓名排序,并打印每名员工的信息
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器