Java 高级排序(Comparator 和 Comparable)
Java 高级排序
在 列表排序章节 中,您学习了如何按字母顺序和数字顺序对列表进行排序,但如果列表中包含对象呢?
要对对象进行排序,您需要指定一个决定对象如何排序的规则。例如,如果您有一系列汽车,您可能希望按年份对它们进行排序,规则可以是较早年份的汽车在前。
Comparator
和 Comparable
接口允许您指定用于对对象进行排序的规则。
能够指定排序规则也允许您更改字符串和数字的排序方式。
Comparators(比较器)
实现 Comparator
接口的对象称为比较器。
Comparator
接口允许您创建一个具有 compare()
方法的类,该方法比较两个对象以决定哪个对象在列表中应该排在前面。
compare()
方法应返回一个数字,该数字是
- 负数,如果第一个对象应该排在列表前面。
- 正数,如果第二个对象应该排在列表前面。
- 零,如果顺序无关紧要。
实现 Comparator
接口的类可能看起来像这样
// Sort Car objects by year
class SortByYear implements Comparator {
public int compare(Object obj1, Object obj2) {
// Make sure that the objects are Car objects
Car a = (Car) obj1;
Car b = (Car) obj2;
// Compare the objects
if (a.year < b.year) return -1; // The first car has a smaller year
if (a.year > b.year) return 1; // The first car has a larger year
return 0; // Both cars have the same year
}
}
要使用比较器,请将其作为参数传递给排序方法
// Use a comparator to sort the cars
Comparator myComparator = new SortByYear();
Collections.sort(myCars, myComparator);
以下是使用比较器按年份对汽车列表进行排序的完整示例
示例
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
// Define a Car class
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;
}
}
// Create a comparator
class SortByYear implements Comparator {
public int compare(Object obj1, Object obj2) {
// Make sure that the objects are Car objects
Car a = (Car) obj1;
Car b = (Car) obj2;
// Compare the year of both objects
if (a.year < b.year) return -1; // The first car has a smaller year
if (a.year > b.year) return 1; // The first car has a larger year
return 0; // Both cars have the same year
}
}
public class Main {
public static void main(String[] args) {
// Create a list of cars
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));
// Use a comparator to sort the cars
Comparator myComparator = new SortByYear();
Collections.sort(myCars, myComparator);
// Display the cars
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) {
// Make sure the objects are integers
Integer a = (Integer)obj1;
Integer b = (Integer)obj2;
// Check each number to see if it is even
// A number is even if the remainder when dividing by 2 is 0
boolean aIsEven = (a % 2) == 0;
boolean bIsEven = (b % 2) == 0;
if (aIsEven == bIsEven) {
// If both numbers are even or both are odd then use normal sorting rules
if (a < b) return -1;
if (a > b) return 1;
return 0;
} else {
// If a is even then it goes first, otherwise b goes first
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;
// Decide how this object compares to other objects
public int compareTo(Object obj) {
Car other = (Car)obj;
if(year < other.year) return -1; // This object is smaller than the other one
if(year > other.year) return 1; // This object is larger than the other one
return 0; // Both objects are the same
}
}
这是与之前相同的示例,但使用了 Comparable
接口而不是比较器
示例
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
// Define a Car class which is comparable
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;
}
// Decide how this object compares to other objects
public int compareTo(Object obj) {
Car other = (Car)obj;
if(year < other.year) return -1; // This object is smaller than the other one
if(year > other.year) return 1; // This object is larger than the other one
return 0; // Both objects are the same
}
}
public class Main {
public static void main(String[] args) {
// Create a list of cars
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));
// Sort the cars
Collections.sort(myCars);
// Display the cars
for (Car c : myCars) {
System.out.println(c.brand + " " + c.model + " " + c.year);
}
}
}
自己动手试一试 »
一个常见的排序技巧
最自然地对两个数字进行排序的方法是像这样写
if(a.year < b.year) return -1; // a is less than b
if(a.year > b.year) return 1; // a is greater than b
return 0; // a is equal to b
但实际上可以用一行代码完成
return a.year - b.year;
这个技巧也可以用来轻松地反向排序
return b.year - a.year;
Comparator 与 Comparable 的区别
比较器是一个只有一个方法且用于比较两个不同对象 Object。
可比较对象是其本身可以与其它对象进行比较的对象。
如果可能,使用 Comparable
接口会更容易,但 Comparator
接口功能更强大,因为它允许您对任何类型的对象进行排序,即使您无法更改其代码。