Java Comparable和Comparator接口

Java Comparable和Comparator接口

我们经常需要在Java程序中比较两个值。比较原始值如intcharfloat是很容易的,像<>==`等。

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

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

一旦定义了如何使用任何这些接口比较对象,您就可以使用各种库函数(如Collections.sort或)对它们进行排序Arrays.sort

[TOC]

Java Comparable接口

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

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

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

compareTo()在类中定义方法时,需要确保此方法的返回值为 -

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

许多预定义的Java类,如StringDateLocalDateLocalDateTime等落实Comparable来定义它们的实例的排序界面。

现在让我们看一个让事情更清晰的例子。

Java Comparable接口示例

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

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

class Employee implements Comparable {
    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来比较两个员工。

我们只是在返回this.getId() - anotherEmployee.getId()compareTo()功能,这将是

  • 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 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我们在上一节中看到的接口定义了类对象的默认顺序。此默认排序也称为对象的自然排序

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

您无法更改compareTo()函数的实现,因为它会影响到处的排序,而不仅仅是因为您的特定要求。

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

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

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

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

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

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

让我们看一个让事情变得清晰的例子。

Java Comparator接口示例

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

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 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 employeeNameComparator = new Comparator() {
            @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 employeeSalaryComparator = new Comparator() {
            @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 employeeJoiningDateComparator = new Comparator() {
            @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 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}]

结论

在本文中,您了解了Java的Comparable和Comparator接口。我们首先理解这些接口,然后查看各种示例以了解如何在实践中使用它们。

我希望这篇文章对你有用。

你可能感兴趣的:(Java Comparable和Comparator接口)