Java 高级排序(Comparator 和 Comparable)
Java 高级排序
在列表排序章节中,您学习了如何按字母顺序和数字顺序对列表进行排序,但如果列表中包含对象呢?
要对对象进行排序,您需要指定一个排序规则来决定对象的排序方式。例如,如果您有一个汽车列表,您可能希望按年份对它们进行排序,那么规则可以是年份较早的汽车排在前面。
Comparator 和 Comparable 接口允许您指定用于对对象进行排序的规则。
能够指定排序规则还可以让您更改字符串和数字的排序方式。
比较器
实现了 Comparator 接口的对象称为比较器。
Comparator 接口允许您创建一个类,该类具有 compare() 方法,该方法比较两个对象以确定哪个对象应该在列表中排在前面。
compare() 方法应返回一个数字,该数字为:
- 如果列表中第一个对象应该放在最前面,则为负值。
- 如果列表中第二个对象应该放在最前面,则为正值。
- 如果顺序无关紧要,则为零。
实现了 Comparator 接口的类可能如下所示:
// 按年份对 Car 对象进行排序
class SortByYear implements Comparator {
public int compare(Object obj1, Object obj2) {
// 确保对象是 Car 对象
Car a = (Car) obj1;
Car b = (Car) obj2;
// 比较对象
if (a.year < b.year) return -1; // 第一辆车的年份较短
if (a.year > b.year) return 1; // 第一辆车年份更长
return 0; 两辆车年份相同。
}
}
要使用比较器,请将其作为参数传递给排序方法:
// 使用比较器对汽车进行排序
Comparator myComparator = new SortByYear();
Collections.sort(myCars, myComparator);
以下是一个使用比较器按年份对汽车列表进行排序的完整示例:
示例
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
// 定义一个 Car 类
class Car {
public String brand;
public String model;
public int year;
public Car(String b, String m, int y) {
brand = b;
model = m;
year = y;
}
}
// 创建一个比较器
class SortByYear implements Comparator {
public int compare(Object obj1, Object obj2) {
// 确保对象是 Car 对象
Car a = (Car) obj1;
Car b = (Car) obj2;
// 比较两个对象的年份
if (a.year < b.year) return -1; // 第一辆车的年份较短
if (a.year > b.year) return 1; // 第一辆车年份更长
return 0; // Both cars have the same year
}
}
public class Main {
public static void main(String[] args) {
// 创建车辆列表
ArrayList<Car> myCars = new ArrayList<Car>();
myCars.add(new Car("BMW", "X5", 1999));
myCars.add(new Car("Honda", "Accord", 2006));
myCars.add(new Car("Ford", "Mustang", 1970));
// 使用比较器对汽车进行排序
Comparator myComparator = new SortByYear();
Collections.sort(myCars, myComparator);
// 显示车辆
for (Car c : myCars) {
System.out.println(c.brand + " " + c.model + " " + c.year);
}
}
}
亲自试一试 »
使用 Lambda 表达式
为了使代码更简洁,可以将比较器替换为 lambda 表达式,该表达式具有与 compare() 方法相同的参数和返回值:
示例
使用 lambda 表达式作为比较器:
Collections.sort(myCars, (obj1, obj2) -> {
Car a = (Car) obj1;
Car b = (Car) obj2;
if (a.year < b.year) return -1;
if (a.year > b.year) return 1;
return 0;
});
亲自试一试 »
特殊排序规则
比较器还可以用于为字符串和数字创建特殊的排序规则。在这个例子中,我们使用比较器将所有偶数排在奇数之前:
示例
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class SortEvenFirst implements Comparator {
public int compare(Object obj1, Object obj2) {
// 请确保对象为整数。
Integer a = (Integer)obj1;
Integer b = (Integer)obj2;
// 检查每个数字是否为偶数
// 如果一个数除以 2 的余数为 0,则称该数为偶数
boolean aIsEven = (a % 2) == 0;
boolean bIsEven = (b % 2) == 0;
if (aIsEven == bIsEven) {
// 如果两个数字都是偶数或都是奇数,则使用常规排序规则
if (a < b) return -1;
if (a > b) return 1;
return 0;
} else {
// 如果 a 为偶数,则 a 先行;否则,b 先行。
if (aIsEven) {
return -1;
} else {
return 1;
}
}
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Integer> myNumbers = new ArrayList<Integer>();
myNumbers.add(33);
myNumbers.add(15);
myNumbers.add(20);
myNumbers.add(34);
myNumbers.add(8);
myNumbers.add(12);
Comparator myComparator = new SortEvenFirst();
Collections.sort(myNumbers, myComparator);
for (int i : myNumbers) {
System.out.println(i);
}
}
}
亲自试一试 »
Comparable 接口
Comparable 接口允许对象使用 compareTo() 方法指定自己的排序规则。
compareTo() 方法接受一个对象作为参数,并将该对象与可比较对象进行比较,以确定哪个对象应该排在列表的前面。
与比较器类似,compareTo() 方法返回一个数字,该数字为:
- 如果比较对象应该放在列表的最前面,则为负值。
- 如果比较对象应该放在列表的最前面,则为正值。
- 如果顺序无关紧要,则为零。
许多原生 Java 类实现了 Comparable 接口,例如 String 和 Integer。
这就是为什么字符串和数字不需要比较器就能排序的原因。
实现了 Comparable 接口的对象可能如下所示:
class Car implements Comparable {
public String brand;
public String model;
public int year;
// 判断这个物体与其他物体相比如何
public int compareTo(Object obj) {
Car other = (Car)obj;
if(year < other.year) return -1; // 这个物体比另一个小
if(year > other.year) return 1; // 这个物体比另一个物体大
return 0; // 这两个物体是同一个
}
}
这里是与之前相同的示例,但使用的是 Comparable 接口而不是比较器:
示例
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
// 定义一个可比较的 Car 类
class Car implements Comparable {
public String brand;
public String model;
public int year;
public Car(String b, String m, int y) {
brand = b;
model = m;
year = y;
}
// 判断这个物体与其他物体相比如何
public int compareTo(Object obj) {
Car other = (Car)obj;
if(year < other.year) return -1; // 这个物体比另一个小
if(year > other.year) return 1; // 这个物体比另一个物体大
return 0; // 这两个物体是同一个
}
}
public class Main {
public static void main(String[] args) {
// 创建车辆列表
ArrayList<Car> myCars = new ArrayList<Car>();
myCars.add(new Car("BMW", "X5", 1999));
myCars.add(new Car("Honda", "Accord", 2006));
myCars.add(new Car("Ford", "Mustang", 1970));
// 对车辆进行排序
Collections.sort(myCars);
// 显示车辆
for (Car c : myCars) {
System.out.println(c.brand + " " + c.model + " " + c.year);
}
}
}
亲自试一试 »
一个常用的排序技巧
对两个数字进行排序最直接的方法是像这样写:
if(a.year < b.year) return -1; // a 小于 b
if(a.year > b.year) return 1; // a 大于 b
return 0; // a 等于 b
但实际上只需一行代码即可完成:
return a.year - b.year;
这个技巧还可以用来轻松地反向排序:
return b.year - a.year;
Comparator vs. Comparable(比较器 vs. 可比较对象)
Comparator(比较器)是一个只有一个方法的对象,用于比较两个不同的对象。
Comparable (可比较对象)是一个可以与其他对象进行比较的对象。
如果可能,使用Comparable接口会更方便,但Comparator接口功能更强大,因为它允许你对任何类型的对象进行排序,即使你无法更改其代码。

