你好!今天我们将讨论比较对象。嗯……不过这个问题我们好像已经讨论过不止一次了?:/ 我们知道“
==
”运算符如何工作,以及equals()
and方法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 = "Даша";
List<String> names = new ArrayList<>();
names.add(name1);
names.add(name2);
names.add(name3);
Collections.sort(names);
System.out.println(names);
}
}
控制台输出:
[Даша, Маша, Саша]
如果您还记得该类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;
}
//...геттеры, сеттеры, 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
。而且,很自然地,Java 也不知道这一点!当我们尝试将Collections.sort()
对象列表传递给方法时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(20012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
//ошибка компилятора!
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;
}
//...геттеры, сеттеры, 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
是的话а < b
。好吧,如果结果是== 0
,那么这两个对象是相等的:a == b
。教我们的班级按制造年份对汽车进行分类就像剥梨子一样简单:
@Override
public int compareTo(Car o) {
return this.getManufactureYear() - o.getManufactureYear();
}
这里发生了什么?我们获取一个汽车对象 ( this
),即该汽车的制造年份,并从中减去另一辆车(我们与该对象进行比较的汽车)的制造年份。如果第一辆汽车的制造年份更大,该方法将返回int > 0
。这意味着汽车this >
就是汽车о
。相反,如果第二辆汽车的制造年份 ( о
) 更大,则该方法将返回负数,因此о > 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);
//тут раньше была ошибка
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()
。但如果这对我们来说还不够怎么办?想象一下我们的程序没那么简单。在大多数情况下,汽车的自然分类(我们按制造年份设置)适合我们。但有时我们的客户中也有一些喜欢快速驾驶的人。如果我们准备一份汽车目录供他们选择,则需要按最高速度订购。 例如,我们在 15% 的情况下需要这样的排序。这显然不足以设置Car
按速度而不是制造年份进行自然排序。但我们不能忽视那15%的客户。我们做什么?这里另一个界面可以为我们提供帮助 - Comparator
。就像,Comparable
它是打字的。有什么不同? Comparable
使我们的对象具有“可比性”,并为它们创建在大多数情况下使用的最自然的排序顺序。 Comparator
- 这是一个单独的“比较器”类(翻译有点笨拙,但可以理解)。如果我们需要实现一些特定的排序,我们不必进入类Car
并改变逻辑compareTo()
。相反,我们可以在程序中创建一个单独的比较器类,并教它进行我们需要的排序!
import java.util.Comparator;
public class MaxSpeedCarComparator implements Comparator<Car> {
@Override
public int compare(Car o1, Car o2) {
return o1.getMaxSpeed() - o2.getMaxSpeed();
}
}
正如您所看到的,我们的Comparator
非常简单。只有一个方法compare()
——这是一个接口方法Comparator
,必须实现它。它以两个物体作为输入Car
,并以通常的方式(通过减法)比较它们的最大速度。就像compareTo()
,它返回数字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()
中定义的自然排序。相反,它将应用传递给它的比较器中的排序算法。这给我们带来了什么好处?首先,与书面代码的兼容性。我们创建了一种新的、特定的排序方法,同时保留了当前的排序方法,该方法将在大多数情况下使用。我们根本没有接触过班级。他还是原来的样子: compareTo()
Car
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();
}
//...геттеры, сеттеры, toString()
}
其次,灵活性。我们可以根据需要添加任意数量的种类。比如说,按颜色、速度、重量或汽车在蝙蝠侠电影中使用的次数对汽车进行排序。只需再创建一个就足够了Comparator
。就这样!今天您学习了两个非常重要的机制,您将在实际工作项目中经常使用它们。但是,如您所知,没有实践的理论毫无意义。因此,是时候巩固你的知识并解决几个问题了!:)
GO TO FULL VERSION