Java Comparable 和 Comparator 接口示例

x33g5p2x  于2021-10-16 转载在 Java  
字(11.4k)|赞(0)|评价(0)|浏览(132)

我们经常需要比较 Java 程序中的两个值。比较像 intchar、​​float 这样的原始值非常简单,可以使用 <>、[ [$5$]] 等

但是比较对象有点不同。例如,您将如何比较两个员工?你会如何比较两个学生?

您需要明确定义如何比较用户定义的类的对象。为此,Java 提供了两个接口,称为 ComparableComparator

一旦您定义了如何使用这些接口中的任何一个来比较对象,您将能够使用各种库函数(如 Collections.sortArrays.sort)对它们进行排序。

Java Comparable 接口

默认情况下,用户定义的类是不可比较的。也就是说,它的对象无法比较。为了使对象具有可比性,该类必须实现 Comparable 接口。

Comparable 接口有一个名为 compareTo() 的方法,你需要实现它来定义一个对象如何与提供的对象进行比较 -

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

当你在你的类中定义 compareTo() 方法时,你需要确保这个方法的返回值是 -

  • negative,如果 this 对象小于提供的对象。
  • zero,如果 this 对象等于提供的对象。
  • positive,如果 this 对象大于提供的对象。

许多预定义的 Java 类,如 StringDateLocalDateLocalDateTime 等,都实现了 Comparable 接口来定义它们的实例的顺序。

现在让我们看一个例子,让事情更清楚。

Java Comparable 接口示例

下面的示例展示了如何在用户定义的类中实现 Comparable 接口并定义 compareTo() 方法以使该类的对象具有可比性。

import java.time.LocalDate;
import java.util.Objects;

class Employee implements Comparable<Employee> {
    private int id;
    private String name;
    private double salary;
    private LocalDate joiningDate;

    public Employee(int id, String name, double salary, LocalDate joiningDate) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.joiningDate = joiningDate;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public LocalDate getJoiningDate() {
        return joiningDate;
    }

    public void setJoiningDate(LocalDate joiningDate) {
        this.joiningDate = joiningDate;
    }

    // Compare Two Employees based on their ID
    /** * @param anotherEmployee - The Employee to be compared. * @return A negative integer, zero, or a positive integer as this employee * is less than, equal to, or greater than the supplied employee object. */
    @Override
    public int compareTo(Employee anotherEmployee) {
        return this.getId() - anotherEmployee.getId();
    }

    // Two employees are equal if their IDs are equal
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return id == employee.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                ", joiningDate=" + joiningDate +
                '}';
    }
}

在上面的例子中,我们通过他们的 ID 比较两个员工。

我们只是从 compareTo() 函数返回 this.getId() - anotherEmployee.getId(),这将是

  • negative 如果此员工的 ID 小于提供的员工的 ID,
  • zero 如果此员工的 ID 等于提供的员工的 ID,并且
  • positive 如果此员工的 ID 大于提供的员工的 ID。

这只是写下以下内容的简洁方式 -

public int compareTo(Employee anotherEmployee) {
    if(this.getId() < anotherEmployee.getId()) {
        return -1;
    } else if (this.getId() > anotherEmployee.getId()) {
        return 1;
    } else {
        return 0;
    }
}

现在让我们看看 Employee 对象如何通过 Collections.sort 方法自动排序 -

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparableExample {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();

        employees.add(new Employee(1010, "Rajeev", 100000.00, LocalDate.of(2010, 7, 10)));
        employees.add(new Employee(1004, "Chris", 95000.50, LocalDate.of(2017, 3, 19)));
        employees.add(new Employee(1015, "David", 134000.00, LocalDate.of(2017, 9, 28)));

        System.out.println("Employees (Before Sorting) : " + employees);

        // This will use the `compareTo()` method of the `Employee` class to compare two employees and sort them.
        Collections.sort(employees);

        System.out.println("\nEmployees (After Sorting) : " + employees);
    }
}
# Output
Employees (Before Sorting) : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]
Employees (After Sorting) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]

任何实现 Comparable 接口的类都可以使用 Sorted Sets 和 Sorted Maps 开箱即用。

Java Comparator 接口

我们在上一节中看到的 Comparable 接口定义了类对象的默认顺序。这种默认排序也称为对象的自然排序

但是,如果您只需要为单个需求更改默认排序呢?例如,如果您想根据名称而不是 ID 对前面示例中的 Employee 对象进行排序,该怎么办?

您不能更改 compareTo() 函数的实现,因为它会影响所有地方的排序,而不仅仅是针对您的特定要求。

此外,如果您正在处理预定义的 Java 类或第三方库中定义的类,您将无法更改默认顺序。例如, String 对象的默认排序是按字母顺序排列。但是,如果您想根据它们的长度对它们进行排序怎么办?

对于这种情况,Java 提供了一个 Comparator 接口。您可以定义一个 Comparator 并将其传递给 Collections.sortArrays.sort 等排序函数,以根据 Comparator 定义的顺序对对象进行排序。

Comparator 接口包含一个名为 compare() 的方法,您需要实现该方法以定义类的对象的顺序 -

public interface Comparator<T> {
    int compare(T o1, T o2);
}

compare() 方法的实现应该返回

  • 一个负整数,如果第一个参数小于第二个,
  • 零,如果第一个参数等于第二个参数,并且
  • 一个正整数,如果第一个参数大于第二个。

让我们看一个例子来说明问题。

Java Comparator接口示例

让我们看看如何通过定义不同的 Comparators 来根据不同的字段对我们在上一节中定义的 Employee 对象的集合进行排序。

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorExample {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();

        employees.add(new Employee(1010, "Rajeev", 100000.00, LocalDate.of(2010, 7, 10)));
        employees.add(new Employee(1004, "Chris", 95000.50, LocalDate.of(2017, 3, 19)));
        employees.add(new Employee(1015, "David", 134000.00, LocalDate.of(2017, 9, 28)));
        employees.add(new Employee(1009, "Steve", 100000.00, LocalDate.of(2016, 5, 18)));

        System.out.println("Employees : " + employees);

        // Sort employees by Name
        Comparator<Employee> employeeNameComparator = new Comparator<Employee>() {
            @Override
            public int compare(Employee e1, Employee e2) {
                return e1.getName().compareTo(e2.getName());
            }
        };

        /* The above Comparator can also be written using lambda expression like so => employeeNameComparator = (e1, e2) -> e1.getName().compareTo(e2.getName()); Which can be shortened even further using Java 8 Comparator default method employeeNameComparator = Comparator.comparing(Employee::getName) */

        Collections.sort(employees, employeeNameComparator);
        System.out.println("\nEmployees (Sorted by Name) : " + employees);

        // Sort employees by Salary
        Comparator<Employee> employeeSalaryComparator = new Comparator<Employee>() {
            @Override
            public int compare(Employee e1, Employee e2) {
                if(e1.getSalary() < e2.getSalary()) {
                    return -1;
                } else if (e1.getSalary() > e2.getSalary()) {
                    return 1;
                } else {
                    return 0;
                }
            }
        };
        
        Collections.sort(employees, employeeSalaryComparator);
        System.out.println("\nEmployees (Sorted by Salary) : " + employees);

        // Sort employees by JoiningDate
        Comparator<Employee> employeeJoiningDateComparator = new Comparator<Employee>() {
            @Override
            public int compare(Employee e1, Employee e2) {
                return e1.getJoiningDate().compareTo(e2.getJoiningDate());
            }
        };

        Collections.sort(employees, employeeJoiningDateComparator);
        System.out.println("\nEmployees (Sorted by JoiningDate) : " + employees);
    }
}
# Output

Employees : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}]
Employees (Sorted by Name) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}]
Employees (Sorted by Salary) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]
Employees (Sorted by JoiningDate) : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]

正如你在上面的例子中看到的,所有的排序方法也接受一个 Comparator 接口的实例。他们使用 Comparator 接口的 compare() 函数定义的排序来对对象进行排序。

使用 Java 8 Comparator 默认方法

Comparator 接口包含用于创建 Comparator 实例的各种默认工厂方法。

通过使用这些工厂方法,我们在上一节中创建的所有 Comparators 都可以变得更加简洁。

这是我们在上一节中使用 Java 8 Comparator 默认方法看到的相同 Comparator 示例 -

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorExample {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();

        employees.add(new Employee(1010, "Rajeev", 100000.00, LocalDate.of(2010, 7, 10)));
        employees.add(new Employee(1004, "Chris", 95000.50, LocalDate.of(2017, 3, 19)));
        employees.add(new Employee(1015, "David", 134000.00, LocalDate.of(2017, 9, 28)));
        employees.add(new Employee(1009, "Steve", 100000.00, LocalDate.of(2016, 5, 18)));

        System.out.println("Employees : " + employees);

        // Sort employees by Name
        Collections.sort(employees, Comparator.comparing(Employee::getName));
        System.out.println("\nEmployees (Sorted by Name) : " + employees);

        // Sort employees by Salary
        Collections.sort(employees, Comparator.comparingDouble(Employee::getSalary));
        System.out.println("\nEmployees (Sorted by Salary) : " + employees);

        // Sort employees by JoiningDate
        Collections.sort(employees, Comparator.comparing(Employee::getJoiningDate));
        System.out.println("\nEmployees (Sorted by JoiningDate) : " + employees);

        // Sort employees by Name in descending order
        Collections.sort(employees, Comparator.comparing(Employee::getName).reversed());
        System.out.println("\nEmployees (Sorted by Name in descending order) : " + employees);

        // Chaining multiple Comparators
        // Sort by Salary. If Salary is same then sort by Name
        Collections.sort(employees, Comparator.comparingDouble(Employee::getSalary).thenComparing(Employee::getName));
        System.out.println("\nEmployees (Sorted by Salary and Name) : " + employees);
    }
}
# Output

Employees : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}]
Employees (Sorted by Name) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}]
Employees (Sorted by Salary) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]
Employees (Sorted by JoiningDate) : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]
Employees (Sorted by Name in descending order) : [Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}]
Employees (Sorted by Salary and Name) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]

相关文章