Java示例中的Comparable和Comparator可以比较
Java中的Comparable和Comparator对于排序对象的集合非常有用。Java提供了一些内置方法来对原始类型数组、包装类数组或列表进行排序。在这里,我们首先学习如何对原始类型和包装类的数组/列表进行排序,然后使用java.lang.Comparable和java.util.Comparator接口来对自定义类的数组/列表进行排序。让我们看看如何使用简单的程序来对原始类型或对象数组和列表进行排序。
package com.Olivia.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.Olivia.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 + "]";
}
}
这是我用来对员工对象数组进行排序的代码。
//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.Olivia.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.Olivia.sort.JavaSorting.main(JavaSorting.java:41)
可比较的和比较器
Java提供了Comparable接口,如果我们想要使用Arrays或Collections的排序方法,就需要实现这个自定义类。Comparable接口具有compareTo(T obj)方法,这个方法由排序方法使用,您可以查看任何包装类、字符串或日期类来确认这一点。我们应该以这样一种方式重写这个方法,使其返回一个负整数、零或正整数,以指示“this”对象是否小于、等于或大于传递的对象。在Employee类中实现Comparable接口之后,即可得到如下的Employee类。
package com.Olivia.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.Olivia.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接口的单独类实现,它将首先比较两个员工对象的ID,如果相同,则再比较它们的姓名。
package com.Olivia.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;
}
}
这是测试类,我们在java中使用Comparable和Comparator的不同方式来对对象进行排序。
package com.Olivia.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中提供对象的排序。
可比性与比较器
-
- 可比对接口可用于提供单一的排序方式,而比较器接口用于提供不同的排序方式。
-
- 使用可比对接口,类需要实现它,而使用比较器不需要对类进行任何更改。
-
- 可比对接口在java.lang包中,而比较器接口在java.util包中。
- 对于使用可比对接口,我们不需要在客户端代码中进行任何更改,Arrays.sort() 或 Collection.sort() 方法会自动使用类的compareTo()方法。对于比较器,客户端需要提供比较器类来在compare()方法中使用。
你知道Collections.sort()方法是基于策略模式的吗?
你可以从我们的GitHub存储库中获取完整的代码和更多Java核心示例。