Java 示例中的 Comparable 和 Comparator
Java 中的 Comparable 和 Comparator 对于对对象集合进行排序非常有用。Java 提供了一些内置方法来对原始类型数组或包装器类数组或列表进行排序。在这里,我们将首先学习如何对原始类型和包装器类的数组/列表进行排序,然后我们将使用java.lang.Comparable和java.util.Comparator接口对自定义类的数组/列表进行排序。让我们看看如何使用一个简单的程序对原始类型或对象数组和列表进行排序。
package com.journaldev.sort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class JavaObjectSorting {
/**
* This class shows how to sort primitive arrays,
* Wrapper classes Object Arrays
* @param args
*/
public static void main(String[] args) {
//sort primitives array like int array
int[] intArr = {5,9,1,10};
Arrays.sort(intArr);
System.out.println(Arrays.toString(intArr));
//sorting String array
String[] strArr = {"A", "C", "B", "Z", "E"};
Arrays.sort(strArr);
System.out.println(Arrays.toString(strArr));
//sorting list of objects of Wrapper classes
List<String> strList = new ArrayList<String>();
strList.add("A");
strList.add("C");
strList.add("B");
strList.add("Z");
strList.add("E");
Collections.sort(strList);
for(String str: strList) System.out.print(" "+str);
}
}
上述程序的输出是:
[1, 5, 9, 10]
[A, B, C, E, Z]
A B C E Z
现在让我们尝试对对象数组进行排序。
package com.journaldev.sort;
public class Employee {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
//this is overridden to print the user-friendly information about the Employee
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
这是我用来对 Employee 对象数组进行排序的代码。
//sorting object array
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//sorting employees array using Comparable interface implementation
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
当我尝试运行它时,它引发以下运行时异常。
Exception in thread "main" java.lang.ClassCastException: com.journaldev.sort.Employee cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at com.journaldev.sort.JavaSorting.main(JavaSorting.java:41)
Compaable 和 Comparator
Java 提供了Comparable接口,如果我们想使用数组或集合排序方法,任何自定义类都应该实现该接口。Comparable 接口具有compareTo(T obj)方法,该方法由排序方法使用,您可以检查任何 Wrapper、String 或 Date 类来确认这一点。我们应该覆盖此方法,以便如果“this”对象小于、等于或大于作为参数传递的对象,则返回负整数、零或正整数。在 Employee 类中实现 Comparable接口后,结果为 Employee 类。
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable<Employee> {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
//let's sort the employee based on an id in ascending order
//returns a negative integer, zero, or a positive integer as this employee id
//is less than, equal to, or greater than the specified object.
return (this.id - emp.id);
}
@Override
//this is required to print the user-friendly information about the Employee
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
现在,当我们执行上述代码片段对员工数组进行排序并打印时,这里是输出。
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
如您所见,Employees 数组按 id 升序排序。但是,在大多数实际场景中,我们希望根据不同的参数进行排序。例如,作为 CEO,我想根据薪水对员工进行排序,而 HR 则希望根据年龄对员工进行排序。在这种情况下,我们需要使用Java Comparator接口,因为Comparable.compareTo(Object o)方法实现可以提供默认排序,我们无法动态更改它。而使用 Comparator,我们可以定义具有不同排序方式的多种方法,然后根据我们的需求选择排序方法。
Java 比较器
需要实现Comparator 接口compare(Object o1, Object o2)方法,该方法接受两个 Object 参数,应以这样的方式实现:如果第一个参数小于第二个参数,则返回负整数;如果它们相等,则返回零;如果第一个参数大于第二个参数,则返回正整数。Comparable 和 Comparator 接口使用泛型进行编译时类型检查,了解有关Java 泛型的更多信息。下面介绍如何在 Employee 类中创建不同的 Comparator 实现。
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator<Employee> NameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
上述所有 Comparator 接口的实现都是匿名类。我们可以使用这些比较器将参数传递给 Arrays 和 Collections 类的排序函数。
//sort employees array using Comparator by Salary
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//sort employees array using Comparator by Age
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//sort employees array using Comparator by Name
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
以下是上述代码片段的输出:
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
所以现在我们知道,如果我们想要对 Java 对象数组或列表进行排序,我们需要实现 Java Comparable 接口来提供默认排序,并且我们应该实现 Java Comparator 接口来提供不同的排序方式。我们还可以创建实现Comparator
接口的单独类,然后使用它。下面是我们解释Java 中的Comparable 和 Comparator 的最终类。
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable<Employee> {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
//let's sort the employee based on an id in ascending order
//returns a negative integer, zero, or a positive integer as this employee id
//is less than, equal to, or greater than the specified object.
return (this.id - emp.id);
}
@Override
//this is required to print the user-friendly information about the Employee
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator<Employee> NameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
}
这是 Comparator 接口的单独类实现,它将首先比较两个 Employees 对象的 id,如果它们相同,则比较名称。
package com.journaldev.sort;
import java.util.Comparator;
public class EmployeeComparatorByIdAndName implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
int flag = o1.getId() - o2.getId();
if(flag==0) flag = o1.getName().compareTo(o2.getName());
return flag;
}
}
这是测试类,我们使用 Comparable 和 Comparator 以不同的方式对 Java 中的对象进行排序。
package com.journaldev.sort;
import java.util.Arrays;
public class JavaObjectSorting {
/**
* This class shows how to sort custom objects array/list
* implementing Comparable and Comparator interfaces
* @param args
*/
public static void main(String[] args) {
//sorting custom object array
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//sorting employees array using Comparable interface implementation
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
//sort employees array using Comparator by Salary
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//sort employees array using Comparator by Age
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//sort employees array using Comparator by Name
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
//Employees list sorted by ID and then name using Comparator class
empArr[0] = new Employee(1, "Mikey", 25, 10000);
Arrays.sort(empArr, new EmployeeComparatorByIdAndName());
System.out.println("Employees list sorted by ID and Name:\n"+Arrays.toString(empArr));
}
}
以下是上述程序的输出:
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by ID and Name:
[[id=1, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000]]
java.lang.Comparable和java.util.Comparator是强大的接口,可用于提供 Java 中的排序对象。
可比较与比较器
- Comparable 接口可用于提供单一排序方式,而 Comparator 接口用于提供不同的排序方式。
- 要使用 Comparable,Class 需要实现它,而要使用 Comparator,我们不需要在类中做任何更改。
- Comparable 接口在
java.lang
包中,而 Comparator 接口存在于java.util
包中。 - 我们不需要在客户端进行任何代码更改即可使用 Comparable,
Arrays.sort()
或者Collection.sort()
方法会自动使用compareTo()
类的方法。对于 Comparator,客户端需要提供 Comparator 类以在 compare() 方法中使用。
您是否知道采用 Comparator 参数的 Collections.sort() 方法遵循策略模式?
您可以从我们的GitHub 存储库中查看完整的代码和更多核心 Java 示例。