第十一章《Java实战常用类》第9节:Comparable接口和Comparator接口

时间:2023-01-02 13:58:43

​对事物进行比较往往是希望对它们进行排序,因此排序的结果是由比较的结果产生的。而对事物进行比较就需要明确比较的内容是什么。例如某学校择优录取考生,就要比较所有考生的成绩,然后排列出考分的高低,而学生入学之后可能又要根据身高来排列出座位的次序。因此对两个学生进行比较,就必须先要明确到底是比较他们的考试成绩还是比较他们的身高。如果希望对两个类型相同的对象进行比较,也需要在类当中明确定义出比较规则,否则就会因没有比较规则而无法完成比较。

Comparable接口就是用于定义比较规则的接口,一个类如果实现了Comparable接口,就必须实现接口中的compareTo()抽象方法,这个方法专门用来制定比较规则,一个类如果没有实现Comparable接口,Java语言就认为这个类没有制定明确的比较规则,它的对象不能相互比较。如何在compareTo()方法中制定比较规则呢?通常情况下,程序员都是在compareTo()方法中比较两个对象的某些属性,然后根据比较结果按要求返回相应的数值。如果a对象调用自身的compareTo()方法与b对象进行比较,那么比较的语句应该是:​

a.compareTo(b);

compareTo()方法的返回值为int型,规则要求:如果经过比较认为a比b小,那么程序员就要让compareTo()方法返回一个负数,而如果a与b相等,则返回0,如果a比b大,返回正数。compareTo()方法就是通过返回值的正负属性反映出a和b哪一个对象更大。下面的【例11_27】展示了如何对两个对象进行比较。​

【例11_27对象的比较1】

Exam11_27.java​

import java.util.Arrays;
class Stone implements Comparable{
String name;//石头的名字
double hardness;//石头的硬度
double weight;//石头的质量
public Stone(String name,double hardness,double weight){
this.name = name;
this.hardness = hardness;
this.weight = weight;
}
@Override
//比较两个石头
public int compareTo(Object o) {
Stone stone = (Stone) o;
if(this.hardness<stone.hardness){
return -1;
}else if(this.hardness==stone.hardness){
return 0;
}else{
return 1;
}
}
@Override
public String toString(){
return this.name;
}
}
public class Exam11_27 {
public static void main(String[] args) {
Stone s1 = new Stone("a石头",2.0,5.0);
Stone s2 = new Stone("b石头",1.5,8.0);
Stone s3 = new Stone("c石头",1.0,7.0);
Stone[] stones = {s1,s2,s3};
System.out.println("按照硬度比较a石头和b石头:"+s1.compareTo(s2));
Arrays.sort(stones);//按照硬度从小到达排列三块石头
System.out.print("按照硬度从小到达排列三块石头:");
System.out.println(Arrays.toString(stones));
}
}

【例11_27】中定义了两个类,其中Stone类表示石头,每个Stone类对象都有hardness和weight属性,它们分别代表石头的硬度和重量。Stone类中的compareTo()方法所定义的比较规则是比较两块石头的硬度而不是重量。在Exam11_27类的main()方法中比较了s1和s2这两块石头,并且调用sort()方法按照硬度的从小到大排列了三块石头。【例11_27】的运行结果如图11-26所示。​

第十一章《Java实战常用类》第9节:Comparable接口和Comparator接口

图11-26【例11_27】运行结果​

从图11-26可以看出,a石头比b石头的硬度值高,所以compareTo()方法返回了一个正数,并且调用sort()方法还能对三块石头按照硬度从小到大完成排序。此处需要特别说明:如果一个类没有实现Comparable接口,那么这个类的对象所组成的数组不能调用sort()方法进行排序,否则会抛出异常,这是因为排序过程中需要对每个数组元素进行比较,没有实现Comparable接口的类不具备比较规则。​

有的时候,程序员要对某些类的对象进行比较,但这些类并没有实现Comparable接口,而程序员因为各种原因也无法修改这些类的源代码,例如程序员无法修改基础类库中的类,在这种情况下就必须借助Comparator接口完成对象的比较。Comparator接口位于java.util包,它定义了一个compare()抽象方法,这个方法从理论上来讲能够制定任何类型的两个对象的比较规则。compare()方法有两个参数,这两个参数就代表了两个被比较的对象。为方便讲述,此处把compare()方法的第一个参数称为a,而把第二个参数称为b。按照规则:当a小于b时,程序员要让compare()方法返回一个负数,而当a等于b时,compare()方法返回0,当a大于b时,compare()方法返回正数。在实际编码过程中,程序员首先要定义一个类实现Comparator接口,然后创建这个类的对象,并调用这个对象的compare()方法比较两个对象。下面的【例11_28】展示了如何使用Comparator接口比较两个对象。​

【例11_28对象的比较2】

Exam11_28.java​

import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
class Box{
String name;//箱子的名字
double volume;//箱子的体积
public Box(String name,double volume){
this.name = name;
this.volume = volume;
}
@Override
public String toString(){
return this.name;
}
}
class BoxComparator implements Comparator<Box> {
@Override
//比较两个箱子
public int compare(Box box1, Box box2) {
if(box1.volume<box2.volume){
return -1;
}else if(box1.volume==box2.volume){
return 0;
}else{
return 1;
}
}
}
public class Exam11_28 {
public static void main(String[] args) {
Box box1 = new Box("a箱子",3.0);
Box box2 = new Box("b箱子",5.0);
Box box3 = new Box("c箱子",2.0);
Box[] boxes = {box1,box2,box3};
BoxComparator boxComparator = new BoxComparator();
System.out.print("按照体积比较a箱子和b箱子1:");
System.out.println(boxComparator.compare(box1,box2));//①
System.out.print("按照体积比较a箱子和b箱子2:");
System.out.println(Objects.compare(box1,box2,boxComparator));//②
Arrays.sort(boxes,boxComparator);//③按照体积从小到达排列三个箱子
System.out.print("按照体积从小到达排列三个箱子:");
System.out.println(Arrays.toString(boxes));
}
}

Exam11_28.java这个源文件中定义了三个类,其中Box类表示箱子,而BoxComparator类实现了Comparator接口。可以看到,BoxComparator类在实现Comparator接口时把类型参数确定为Box,这样的话BoxComparator类就成了专门比较Box类对象的比较器。在Exam11_28类的main()方法中,语句①用BoxComparator类的compare()方法比较两个Box类对象,而语句②用Objects类的compare()方法比较两个Box对象,由于Box类中本身没有定义比较对象的方法,所以还要为compare()方法传递BoxComparator类对象作为参数,这样compare()方法才能正确的完成对Box对象的比较。语句③调用Arrays类的sort()方法按照体积从小到大的方式排列了三个Box对象,也是由于Box类没有定义比较对象的方法,所以要给sort()方法传递BoxComparator类对象作为参数。【例11_28】的运行结果如图11-27所示。​

第十一章《Java实战常用类》第9节:Comparable接口和Comparator接口

图11-27【例11_28】运行结果​

从图11-27可以看出:用BoxComparator类的compare()方法和用Objects类的compare()方法比较两个Box对象的效果是完全相同的,这是因为它们本质上都是用BoxComparator类中所定义的compare()方法完成了两个对象的比较过程。​

实际上,程序员通过对Comparator接口的不同实现过程还能够完成对对象的不同方式比较,例如可以定义Comparator接口的三个实现类,这三个实现类可以分别按照箱子的体积、重量、生产日期对箱子进行比较和排序,这样的话就能做到随意指定对象的比较规则。

本文字版教程还配有更详细的视频讲解,小伙伴们可以点击这里观看。