Java Comparable和Comparator接口
我们经常需要在Java程序中比较两个值。比较原始值如int
,char
,float
是很容易的,像<,
>,
==`等。
但比较对象有点不同。例如,您如何比较两名员工?你会如何比较两个学生?
您需要明确定义应如何比较用户定义类的对象。为此,Java提供了两个名为Comparable
和的接口Comparator
。
一旦定义了如何使用任何这些接口比较对象,您就可以使用各种库函数(如Collections.sort
或)对它们进行排序Arrays.sort
。
[TOC]
Java Comparable接口
默认情况下,用户定义的类不具有可比性。也就是说,它的对象无法进行比较。要使对象具有可比性,该类必须实现该Comparable
接口。
该Comparable
接口有一个称为一个方法compareTo()
,你需要以定义对象如何与所提供的对象进行比较来实现-
public interface Comparable<T> {
public int compareTo(T o);
}
compareTo()
在类中定义方法时,需要确保此方法的返回值为 -
-
negative
,如果此对象小于提供的对象。 -
zero
,如果此对象等于提供的对象。 -
positive
,如果此对象大于提供的对象。
许多预定义的Java类,如String
,Date
,LocalDate
,LocalDateTime
等落实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来比较两个员工。
我们只是在返回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<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
我们在上一节中看到的接口定义了类对象的默认顺序。此默认排序也称为对象的自然排序。
但是,如果您只需要针对单个要求更改默认排序,该怎么办?例如,如果您想Employee
根据名称而不是ID对上一个示例中的对象进行排序,该怎么办?
您无法更改compareTo()
函数的实现,因为它会影响到处的排序,而不仅仅是因为您的特定要求。
此外,如果您正在处理预定义的Java类或第三方库中定义的类,您将无法更改默认顺序。例如,String对象的默认顺序是按字母顺序排序。但是如果你想根据它们的长度排序呢?
对于这种情况,Java提供了一个Comparator
接口。您可以定义一个Comparator
并将其传递给排序函数,Collections.sort
或者Arrays.sort
根据由此定义的排序对对象进行排序Comparator
。
该Comparator
接口包含一个被调用的方法compare()
,您需要实现该方法以定义类对象的顺序 -
public interface Comparator<T> {
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<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}]
结论
在本文中,您了解了Java的Comparable和Comparator接口。我们首先理解这些接口,然后查看各种示例以了解如何在实践中使用它们。
我希望这篇文章对你有用。
网友评论