你好!今天我们要谈谈比较对象。
嗯……可是这个话题我们不是已经谈过不止一次了吗?:/ 我们知道
我应该什么时候使用
例如,假设我们有 15% 的时间需要这样排序。这显然不足以让我们将
这样做有什么好处?首先,与现有代码的兼容性。我们创建了一种新的特殊排序方法,同时保留了大部分时间都会使用的现有方法。

==
运算符是如何工作的,以及equals()
和hashCode()
方法。比较有点不一样。以前,我们很可能是指“检查对象是否相等”。但是将对象相互比较的原因可能完全不同!其中最明显的是排序。我想如果你被告知对数字或字符串进行排序ArrayList<>
,你将能够毫无问题地处理这个问题:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
String name1 = "Masha";
String name2 = "Sasha";
String name3 = "Dasha";
List<String> names = new ArrayList<>();
names.add(name1);
names.add(name2);
names.add(name3);
Collections.sort(names);
System.out.println(names);
}
}
控制台输出:
[Dasha, Masha, Sasha]
如果您还记得Collections
类及其sort()
方法,那就太好了!我想你在数字方面也不会有问题。这里有一个更具挑战性的任务:
public class Car {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
// ...getters, setters, toString()
}
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
}
}
任务其实很简单。我们有一个Car
类和 3 个 Car 对象。请您对列表中的汽车进行排序好吗?您可能会问,“它们应该如何排序?” 按名字?按生产年份?按最大速度?很好的问题。目前,我们不知道如何对Car
对象进行排序。而且,很自然地,Java 也不知道这一点!当我们尝试将对象列表传递Car
给该Collections.sort()
方法时,我们得到一个错误:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(20012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
// Compilation error!
Collections.sort(cars);
}
}
事实上,该语言如何知道如何对您编写的类的对象进行排序?这取决于您的程序需要做什么。我们必须以某种方式教 Java 比较这些对象。并按照我们想要的方式比较它们。Java 对此有一个特殊的机制:Comparable
接口。为了以某种方式比较和排序我们的Car
对象,类必须实现这个接口,它由一个方法组成compareTo()
::
public class Car implements Comparable<Car> {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
@Override
public int compareTo(Car o) {
return 0;
}
// ...getters, setters, toString()
}
请注意我们指定了Comparable<Car>
接口,而不仅仅是Comparable
. 这是一个参数化接口,也就是说,我们必须指定具体的关联类。原则上可以<Car>
从界面中移除,但Object
默认情况下会基于对象进行比较。代替方法compareTo(Car o)
,我们的类将有:
@Override
public int compareTo(Object o) {
return 0;
}
当然,我们使用它更容易Car
。在方法内部compareTo()
,我们实现了比较汽车的逻辑。假设我们需要按制造年份对它们进行排序。您可能注意到该compareTo()
方法返回一个int
,而不是一个boolean
。不要让这让你感到惊讶。当我们比较两个对象时,有 3 种可能性:
а < b
a > b
a == b
.
boolean
只有 2 个值:true 和 false,这不适用于比较对象。有了int
,一切都简单多了。如果返回值为> 0
,则a > b
。如果的结果compareTo
是< 0
,那么a < b
。并且,如果结果为== 0
,则两个对象相等:a == b
。教我们的班级按制造年份对汽车进行分类很容易:
@Override
public int compareTo(Car o) {
return this.getManufactureYear() - o.getManufactureYear();
}
但是这里发生了什么?我们获取一个 Car 对象 ( this
),获取这辆车的制造年份,并从中减去另一辆汽车(与该对象进行比较的汽车)的制造年份。如果第一辆汽车的制造年份更大,该方法将返回一个int > 0
. 这意味着this car >
汽车o
。相反,如果第二辆车的制造年份 ( о
) 更大,则该方法将返回一个负数,这意味着o > this
. 最后,如果它们相等,则该方法将返回0
。这个简单的机制已经足够我们对Car
对象集合进行排序了!您无需执行任何其他操作。一探究竟:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
// There was previously an error here
Collections.sort(cars);
System.out.println(cars);
}
}
控制台输出:
[Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310},
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350},
Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}]
汽车按照我们想要的方式分类!:) 
Comparable
?中实现的比较方法Comparable
称为自然排序。这是因为在compareTo()
方法中,您定义了比较此类对象的最常见或最自然的方式。Java 已经有了自然顺序。例如,Java 知道字符串通常按字母顺序排序,而数字则按数值递增排序。因此,如果您sort()
在数字或字符串列表上调用该方法,它们将被排序。如果我们的程序通常按制造年份对汽车进行比较和排序,那么我们应该使用接口Comparable<Car>
和compareTo()
方法。但是,如果这对我们来说还不够怎么办?假设我们的程序不是那么简单。在大多数情况下,汽车的自然分类(我们已将其设置为按制造年份执行)适合我们。但有时我们的客户是快速驾驶的狂热爱好者。如果我们正在准备一份汽车目录供他们阅读,那么汽车应该按最高速度排序。 
Car
班级的自然排序设置为按速度而不是按制造年份。但是我们不能忽视15%的客户。那么我们该怎么办?另一个接口在这里为我们提供帮助:Comparator
。就像Comparable
,它是一个参数化的接口。有什么不同? Comparable
使我们的对象“可比较”并定义它们最自然的排序顺序,即在大多数情况下将使用的排序顺序。 Comparator
是一个单独的“比较”界面。如果我们需要实现某种特殊的排序顺序,我们不需要深入到类中去Car
改变.class的逻辑compareTo()
。相反,我们可以创建一个单独的类来实现 Comparator 并教它如何执行我们需要的排序!
import java.util.Comparator;
public class MaxSpeedCarComparator implements Comparator<Car> {
@Override
public int compare(Car o1, Car o2) {
return o1.getMaxSpeed() - o2.getMaxSpeed();
}
}
如您所见,我们的Comparator
非常简单。我们只需要实现一个接口方法:invoke()
. 它以两个Car
对象作为输入,并以通常的方式(通过减法)比较它们的最大速度。like compareTo()
,返回an int
,比较的原理是一样的。我们如何使用它?一切都很简单:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
Comparator speedComparator = new MaxSpeedCarComparator();
Collections.sort(cars, speedComparator);
System.out.println(cars);
}
}
控制台输出:
[Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290},
Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310},
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}]
Collections.sort()
我们只需创建一个比较器对象并将其与要排序的列表一起 传递给方法。当sort()
方法接收比较器时,它不使用Car
类compareTo()
方法中定义的自然排序。相反,它应用由传递给它的比较器定义的排序算法。 Car
我们根本就没碰过课。它是一个Comparable
,所以它仍然是:
public class Car implements Comparable<Car> {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
@Override
public int compareTo(Car o) {
return this.getManufactureYear() - o.getManufactureYear();
}
// ...getters, setters, toString()
}
第二,灵活性。我们可以添加任意多的排序算法。例如,我们可以按颜色、速度、重量或汽车在蝙蝠侠电影中的使用次数对汽车进行分类。我们需要做的就是创建一个额外的Comparator
. 就是这样!今天你学习了两个非常重要的机制,你在工作中会经常在实际项目中使用它们。但是,如您所知,没有实践的理论是无用的。现在是时候巩固您的知识并完成一些任务了!
GO TO FULL VERSION