ZetCode

Java Comparable 接口

最后修改时间:2025 年 4 月 13 日

java.lang.Comparable 接口定义了 Java 中对象的自然排序。它提供了一个单一方法 compareTo,任何需要排序的类都必须实现该方法。实现此接口允许基于定义的排序逻辑比较对象。

通过实现 Comparable,类的实例获得自动排序功能,这意味着可以使用诸如 Collections.sort 之类的方法按顺序排列它们,或者存储在排序的集合中,例如 TreeSetTreeMap。 这确保了不同排序机制之间的一致行为。

Comparable 接口定义

Comparable 接口很简单,只包含一个确定对象相对顺序的方法

public interface Comparable<T> {
    int compareTo(T o);

泛型类型参数 T 指定了实现类可以与之比较的对象类型。 compareTo 方法将当前对象与相同类型的另一个对象进行比较,并返回

排序中的用法

实现 Comparable 的类必须在 compareTo 内部定义其排序逻辑。 例如,基于年龄对诸如 Person 之类的自定义对象列表进行排序将需要如下重写 compareTo

class Person implements Comparable<Person> {
    private int age;

    public Person(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Person other) {
        return Integer.compare(this.age, other.age);
    }

这确保了对 Person 对象集合进行排序将根据年龄按升序排列它们。 Comparable 接口广泛用于需要对象默认排序逻辑的场景,例如排序列表、维护有序数据结构和简化比较。

基本 Comparable 实现

让我们从一个为 Person 类实现 Comparable 的简单示例开始。 我们将根据他们的年龄比较 Person 对象。

Main.java
class Person implements Comparable<Person> {

    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public int compareTo(Person other) {
        return this.age - other.age;
    }
    
    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}


void main() {

    List<Person> people = Arrays.asList(
        new Person("Alice", 30),
        new Person("Bob", 25),
        new Person("Charlie", 35)
    );
    
    Collections.sort(people);
    System.out.println("Sorted by age: " + people);
}

在此示例中,Person 实现了 Comparable<Person> 并按年龄定义自然排序。 compareTo 方法从这个人的年龄中减去另一个人的年龄。 Collections.sort 使用它来对列表进行排序。

使用 Comparable 进行字符串比较

Java 中的字符串已经实现了 Comparable,允许它们按字母顺序排序。 以下是 String 的自然排序的工作方式。

Main.java
void main() {

    List<String> names = Arrays.asList(
        "Zoe", "Alice", "Bob", "Charlie", "David"
    );
    
    Collections.sort(names);
    System.out.println("Sorted names: " + names);
    
    // Demonstrating compareTo directly
    System.out.println("\"Alice\".compareTo(\"Bob\"): " + 
                        "Alice".compareTo("Bob"));
}

此示例显示了 String 的自然排序。 列表按字母顺序排序。 直接 compareTo 调用显示 "Alice" 在词典编纂上位于 "Bob" 之前,返回一个负数。

比较多个字段

比较对象时,我们经常需要考虑多个字段。 以下是如何实现更复杂的比较逻辑。

Main.java
class Student implements Comparable<Student> {

    private String name;
    private int grade;
    private double gpa;
    
    public Student(String name, int grade, double gpa) {
        this.name = name;
        this.grade = grade;
        this.gpa = gpa;
    }
    
    @Override
    public int compareTo(Student other) {

        // First compare by grade
        int gradeCompare = Integer.compare(this.grade, other.grade);
        if (gradeCompare != 0) {
            return gradeCompare;
        }
        
        // If grades are equal, compare by GPA (descending)
        int gpaCompare = Double.compare(other.gpa, this.gpa);
        if (gpaCompare != 0) {
            return gpaCompare;
        }
        
        // If GPA also equal, compare by name
        return this.name.compareTo(other.name);
    }
    
    @Override
    public String toString() {
        return name + " (Grade " + grade + ", GPA " + gpa + ")";
    }
}

void main() {

    List<Student> students = Arrays.asList(
        new Student("Alice", 10, 3.8),
        new Student("Bob", 10, 3.9),
        new Student("Charlie", 9, 4.0),
        new Student("David", 10, 3.9)
    );
    
    Collections.sort(students);
    System.out.println("Sorted students:");
    students.forEach(System.out::println);
}

这个 Student 类首先按成绩(升序)、然后按 GPA(降序)最后按姓名(升序)进行比较。 该示例展示了如何以清晰、可维护的方式实现多字段比较。

具有自定义对象的 Comparable

这是另一个 Product 类的示例,该类基于价格然后是名称实现 Comparable

Main.java
class Product implements Comparable<Product> {

    private String name;
    private double price;
    
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    @Override
    public int compareTo(Product other) {
        int priceCompare = Double.compare(this.price, other.price);
        if (priceCompare != 0) {
            return priceCompare;
        }
        return this.name.compareTo(other.name);
    }
    
    @Override
    public String toString() {
        return name + " ($" + price + ")";
    }
}

void main() {

    List<Product> products = Arrays.asList(

        new Product("Laptop", 999.99),
        new Product("Phone", 699.99),
        new Product("Tablet", 399.99),
        new Product("Headphones", 99.99),
        new Product("Mouse", 19.99),
        new Product("Keyboard", 99.99),
        new Product("Smartwatch", 399.99),
        new Product("Monitor", 699.99)
    );
    
    Collections.sort(products);
    System.out.println("Products sorted by price:");
    products.forEach(System.out::println);
}

Product 类首先按价格(升序)然后按名称(升序)进行比较。 这确保了即使产品价格相同,也能保持一致的排序。 该示例演示了按自然顺序对产品列表进行排序。

反向自然排序

有时我们需要按与自然顺序相反的顺序进行排序。 以下是如何使用 Collections.reverseOrder() 来实现。

Main.java
void main() {

    List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 3, 9, 4);
    
    // Natural order (ascending)
    Collections.sort(numbers);
    System.out.println("Natural order: " + numbers);
    
    // Reverse natural order (descending)
    Collections.sort(numbers, Collections.reverseOrder());
    System.out.println("Reverse order: " + numbers);
    
    // Strings in reverse alphabetical order
    List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
    Collections.sort(words, Collections.reverseOrder());
    System.out.println("Reverse alphabetical: " + words);
}

此示例显示了如何使用 Collections.reverseOrder 按与自然顺序相反的顺序进行排序。 它适用于任何 Comparable 类型,包括 IntegerString。 由 reverseOrder 返回的比较器只是反转自然比较的结果。

具有 LocalDate 对象的 Comparable

Java 的 LocalDate 类实现了 Comparable,从而能够对日期值进行自然的时间顺序排序。 这是一个演示如何使用 LocalDate 对日期列表进行排序的示例。

Main.java
void main() {

    List<LocalDate> dates = Arrays.asList(
        LocalDate.parse("2023-05-15"),
        LocalDate.parse("2023-01-10"),
        LocalDate.parse("2023-03-20"),
        LocalDate.parse("2023-11-05")
    );

    System.out.println("Original dates:");
    dates.forEach(System.out::println);

    Collections.sort(dates);

    System.out.println("\nSorted dates:");
    dates.forEach(System.out::println);
}

此示例演示了 LocalDate 的自然排序,它遵循时间顺序。 日期从字符串解析,存储在列表中,并使用 Collections.sort 排序。 输出按升序显示日期。

Comparable 与 Comparator

虽然 Comparable 定义了自然排序,但 Comparator 提供了外部比较逻辑。 这是一个展示这两种方法的示例。

Main.java
class Employee implements Comparable<Employee> {

    private String name;
    private int id;
    private String department;
    
    public Employee(String name, int id, String department) {
        this.name = name;
        this.id = id;
        this.department = department;
    }
    
    // Natural ordering by ID
    @Override
    public int compareTo(Employee other) {
        return Integer.compare(this.id, other.id);
    }
    
    public String getName() { return name; }
    public int getId() { return id; }
    public String getDepartment() { return department; }
    
    @Override
    public String toString() {
        return name + " (ID: " + id + ", Dept: " + department + ")";
    }
}

void main() {

    List<Employee> employees = Arrays.asList(
        new Employee("Alice", 103, "HR"),
        new Employee("Bob", 101, "IT"),
        new Employee("Charlie", 102, "Finance")
    );
    
    // Sort using natural order (by ID)
    Collections.sort(employees);
    System.out.println("Sorted by ID (natural order):");
    employees.forEach(System.out::println);
    
    // Sort using Comparator (by name)
    Collections.sort(employees, Comparator.comparing(Employee::getName));
    System.out.println("\nSorted by name (using Comparator):");
    employees.forEach(System.out::println);
}

此示例展示了这两种方法。 Employee 实现了 Comparable,用于按 ID 进行自然排序。 我们还演示了使用 Comparator lambda 按名称排序。 Comparable 用于主要排序,而 Comparator 提供了替代排序选项。

来源

Java Comparable 接口文档

本教程通过实际示例介绍了 Comparable 接口。 实现 Comparable 可以实现自然排序,从而可以使用 Collections.sort 对对象进行排序,并可以在排序的集合中使用。 请记住维护 compareTo 约定以获得一致的行为。

作者

我叫 Jan Bodnar,是一位拥有多年经验的敬业程序员。 我于 2007 年开始撰写编程文章,此后撰写了 1,400 多篇文章和八本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程