很多同学搞不清楚Comparable和Comparator这两个接口,单看这两个单词,一个是形容词一个是名词,但是可以看出来都和比较有关,我个人把实现了Comparable接口的某个类理解成这个类具备了比较能力,而把实现了Comparator的类称为比较器类,那么他们分别该怎么用呢?
Comparable(比较能力)
PriorityQueue队列
在正式介绍他们俩之前,先给大家介绍一下Java中的优先级队列PriorityQueue,这是一个实现了Queue接口的类,它的功能是add()进入一个个同类型对象,然后它会在内部进行操作,保证每次头删的时候都是队列中的最小的元素。既然要给出队列中的最小值,那么PriorityQueue在每次插入之后都会对其内部元素进行排序,才能保证队首是整个队列的最小值。下面演示一下:
import java.util.PriorityQueue;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
Queue<Integer> queue = new PriorityQueue<>();
queue.add(1);
queue.add(999);
queue.add(45);
queue.add(3);
queue.add(66);
System.out.println("对首元素" + queue.element());
queue.remove();
System.out.println("对首元素" + queue.element());
queue.remove();
System.out.println("对首元素" + queue.element());
queue.remove();
System.out.println("对首元素" + queue.element());
queue.remove();
System.out.println("对首元素" + queue.element());
}
}
运行结果: 可以看到每次进行出队列操作后队首元素都是当前队列的最小值,而且这并不是我们插入时的顺序。所以现在可以确定的是每次出队列后PriorityQueue会选出队列中剩余元素中的最小值放在队首,可问题现在PriorityQueue是根据什么来找到的最小值呢? 大家可能会有疑问:1就是最小值啊,999就是最大值啊,这不很明显吗? 可是要知道的是java会对这些基本int类型作自动封装,队列中实际是Integer类的对象,所以问题就是PriorityQueue是怎么比较一个个Integer对象的? 我们点开Java中的Integer类看看:
从图中可以清楚的看到Integer类实现了Comparable接口,好了,说到这里我们来做个简单的总结,PriorityQueue可以将插入的所有元素中的最小值放在队首,怎么判断出哪个元素最小,对中元素实现了Comparable接口才能判断出哪个元素小那个元素大。 要是不实现Comparable接口怎么办呢? 我们来创建一个Student类放到PriorityQueue中看看。
public class Student {
public String name;
public int grade;
public int totalScore;
public Student(String name, int totalScore){
this.name = name;
this.totalScore = totalScore;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", totalScore=" + totalScore +
'}';
}
}
实例化几个student对象,然后试着把他们放到PriorityQueue中看看。
import java.util.PriorityQueue;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
Queue<Student> queue = new PriorityQueue<>();
Student a = new Student("张三", 555);
Student b = new Student("李四", 378);
Student c = new Student("王五", 456);
Student d = new Student("魏六", 555);
queue.add(a);
queue.add(b);
queue.add(c);
queue.add(d);
System.out.println(queue.remove());
}
}
报错了: 可以看到在16行出的错: 从图中可以看到出错行16行是在执行b对象也就是第二个对象的插入时出了问题,为什么在插第一个对象的时候不报错呢?回想一下,PriorityQueue会把队列中的最小的元素放在队首,插入第一个对象时全队列只有一个元素所以不需要找最小对象,插第二个对象时队列中有两个元素了需要选两个中较小的一个的时候就出错了,因为我们Student类没有实现Comparable接口,所以PriorityQueue不能比较对象之间谁大谁小,所以在插入的时候报错了。那我们来实现Comparable接口:
public class Student implements Comparable<Student>{
public String name;
public int grade;
public int totalScore;
public Student(String name, int totalScore){
this.name = name;
this.totalScore = totalScore;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", totalScore=" + totalScore +
'}';
}
@Override
public int compareTo(Student o) {
return 0;
}
}
再来执行上面的插入操作:
public class Main {
public static void main(String[] args) {
Queue<Student> queue = new PriorityQueue<>();
Student a = new Student("张三", 555);
Student b = new Student("李四", 378);
Student c = new Student("王五", 456);
Student d = new Student("魏六", 555);
queue.add(a);
queue.add(b);
queue.add(c);
queue.add(d);
queue.remove();
}
}
可以看到因为我们的Student类实现了Comparable接口插入而插入成功了。并且在做出队列操作时也成功删除了。 我们再来捋一捋:因为我们的Student类实现了Comparable接口才能成功的入PriorityQueue队列,可是这里又有一个问题就是PriorityQueue是怎么知道我们插入的哪个元素是最小的? 这是我们Student类实现的Comparable中唯一的一个方法,PriorityQueue是根据这个方法的返回值比较元素大小的。它会两两对队列内的元素进行比较,谁大谁小是根据这个compareTo方法的返回值决定,比如PriorityQueue开始比较时先从例子中的a对象开始,那么它会再依次向a的compareTo()方法中传入b,c,d对象,这样比下来就知道了a和其他三个对象谁大谁小 ,我们要自己重写这个方法来规定是依据this的什么属性和传入对象的什么属性作比较的。 拿Student类来说它的属性有姓名,年级,总成绩三个,我们可以根据总成绩totalScore来做比较依据重写compareTo()方法。成绩高的的Student对象大,成绩低的对象小,这里比较绕,大家还是要明确我们Student类虽然实现了Comparable接口有了比较能力,能插入PriorityQueue,但是想让程序知道哪个对象大哪个对象小全是根据compareTo()方法的返回值看的。 Java默认的是compareTo()返回值为负则表示传入的o对象大,返回0表示一样大,正数表示this对象大。 我们重写Student类的compareTo()方法:
public class Student implements Comparable<Student>{
public String name;
public int grade;
public int totalScore;
public Student(String name, int totalScore){
this.name = name;
this.totalScore = totalScore;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", totalScore=" + totalScore +
'}';
}
@Override
public int compareTo(Student o) {
return this.totalScore - o.totalScore;
}
}
当返回值为负数时表示this.totalScore < o.totalScore,也就是this对象比o对象小。 当返回值为0时表示this.totalScore = o.totalScore,也就是this对象和o对象一样大。 当返回值为正数数时表示this.totalScore > o.totalScore,也就是this对象比o对象大。
import java.util.PriorityQueue;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
Queue<Student> queue = new PriorityQueue<>();
Student a = new Student("张三", 555);
Student b = new Student("李四", 378);
Student c = new Student("王五", 456);
Student d = new Student("魏六", 555);
queue.add(a);
queue.add(b);
queue.add(c);
queue.add(d);
System.out.println("对首元素" + queue.element());
}
}
说到这里大家应该对Comparable的作用有所了解了,实现了它的类就有了比较能力,但是你想根据什么比较两个对象大小还得看你怎么重写compareTo()方法。如果还是迷的话请多卡看一看上面的叙述。
Comparator(比较器)
比较能力是一个类自己具备的,相应的比较器Comparator是要自己建造的,我们要为需要比较的类量身打造一个实现Comparator接口的比较器类,下面创建一个专为Student类量身打造的比较器: 我们先让Student类丧失比较能力也就是不实现Comparable接口,不重写compareTo()方法。
public class Student {
public String name;
public int grade;
public int totalScore;
public Student(String name, int totalScore){
this.name = name;
this.totalScore = totalScore;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", totalScore=" + totalScore +
'}';
}
}
Student类的比较器类:
import java.util.Comparator;
public class Mycomparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.totalScore - o2.totalScore;
}
}
他是这样用的:
import java.util.PriorityQueue;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
Mycomparator myComparator = new Mycomparator();
Queue<Student> queue = new PriorityQueue<>(myComparator);
Student a = new Student("张三", 555);
Student b = new Student("李四", 378);
Student c = new Student("王五", 456);
Student d = new Student("魏六", 555);
queue.add(a);
queue.add(b);
queue.add(c);
queue.add(d);
System.out.println("对首元素" + queue.element());
}
}
运行结果:
在创建队列的时候就把比较器传进去。
小结
不管是Comparable(比较能力)和Comparator(比较器)都是用于我们实现的类作比较。Comparable是直接实现于我们创建的类,让这个类以后自己天然具备比较能力,Comparator是我们专为某一个类写的一个比较器类,当需要的时候我们得实例化一个比较器然后传入。至于具体是怎么比较那个对象大那个对象小我们就要根据自己的需要来重写方法了,在此提一下Java会根据这个方法的返回值判断两个对象谁大谁小。
对于实现Comparable接口的compareTo()方法来说返回值为正表示传入的对象大,this对象小;返回值为0两个对象一样大;返回值为负,表示传入的对象比this对象大。
对于实现Comparator接口的compare()方法来说返回值为正则传入的第一个对象大于第二个对象,返回值为0表示传入的两个对象一样大;返回值为负表示传入的第一个对象小于第二个对象。
|