Java 高级排序(Comparator 和 Comparable)
Java 高级排序
在 列表排序章节 中,您学习了如何按字母顺序和数字顺序对列表进行排序,但是如果列表中包含对象怎么办?
要排序对象,您需要指定一个规则来决定如何排序对象。例如,如果您有一份汽车列表,您可能希望按年份对其进行排序,规则可能是年份早的汽车排在前面。
The Comparator
和 Comparable
接口允许您指定用于排序对象的规则。
能够指定排序规则还允许您更改字符串和数字的排序方式。
比较器
实现 Comparator
接口的对象称为比较器。
The Comparator
接口允许您创建一个包含 compare()
方法的类,该方法比较两个对象以确定哪个对象应该在列表中排在前面。
The 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 表达式替换,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);
}
}
}
自己试试 »
The 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;
比较器与可比较对象
比较器是一个带有方法的对象,用于比较两个不同的对象。
可比较对象是一个可以与其他对象进行比较的对象。
在可能的情况下,使用 Comparable
接口更方便,但 Comparator
接口功能更强大,因为它允许您对任何类型的对象进行排序,即使您无法更改其代码。