目录
Comparable接口
Comparator接口
Comparable接口
Comparable接口在源码中的声明:
public interface Comparable<T> {
public int compareTo(T o);
}
可以看到,只要一个compareTo方法,也就是说,实现Comparable接口的类都要重写compareTo方法。
Comparable接口的功能就是用于对象之间的比较。那个也就是说比较的主要实现逻辑就在compareTo方法中。
package Blog;
import java.util.Arrays;
class Student implements Comparable<Student> {
public int score;
public String name;
public Student() {
}
public Student(int score, String name) {
this.score = score;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"score=" + score +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Student o) {
return this.score - o.score;
}
}
public class test {
public static void main(String[] args) {
Student student1 = new Student(90, "abc");
Student student2 = new Student(80, "bdc");
System.out.println(student1.compareTo(student2));
}
}
可以看到上面代码和结果,我们有两个student对象,这个两个对象之间我们想要知道谁大,那么我们就可以实现 Comparable 重写compareTo方法。在compareTo方法定义比较规则,this就是那个对象调用的compareTo方法,O就是要和那个对象进行比较。
我们在compareTo方法中定义的是以score来比较的。我们还可以定义以name进行比较。
@Override
public int compareTo(Student o) {
return this.name.compareTo(o.name);
}
上述代码就是以name字段来比较大小。
可以看出在compareTo中再次的调用了compareTo方法,但是需要注意的是,这个compareTo方法不是我们写的这个方法,而是String的compareTo方法。因为name是String类型的。String类也是实现了comparable接口的。
我们经常用到的排序方法,Arrays.sort(); 这个方法是我们一直在使用的。
但是我们基本上使用都是用于基本数据类型,基本数据类型在排序过程进行比较是简单明了的。
那么我们如果排序的是一个对象呢,在排序的过程是如果进行比较的呢?
class Student {
public int score;
public String name;
public Student() {
}
public Student(int score, String name) {
this.score = score;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"score=" + score +
", name='" + name + '\'' +
'}';
}
}
public class test {
public static void main(String[] args) {
Student student1 = new Student(90, "张三");
Student student2 = new Student(80, "王三");
Student student3 = new Student(70, "李三");
Student student4 = new Student(40, "高三");
Student student5 = new Student(50, "马三");
Student[] students = {student1, student2, student3, student4, student5};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
可以看到上述的代码中,我们调用了sort方法来对Student对象数组进行排序。
但是执行代码之后,我们发现报ClassCastException错误了。
这是因为我们的数组是对象数组,里面有很多的不同的属性,那么就造成在排序的过程中编译器不知道我们是以什么规则来进行排序的。
此时为了解决上述问题,就需要用Comparable接口了。实现Comparable接口后,就得重写compareTo方法。
package Blog;
import java.util.Arrays;
class Student implements Comparable<Student> {
public int score;
public String name;
public Student() {
}
public Student(int score, String name) {
this.score = score;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"score=" + score +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Student o) {
return this.score - o.score;
}
}
public class test {
public static void main(String[] args) {
Student student1 = new Student(90, "张三");
Student student2 = new Student(80, "王三");
Student student3 = new Student(70, "李三");
Student student4 = new Student(40, "高三");
Student student5 = new Student(50, "马三");
Student[] students = {student1, student2, student3, student4, student5};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
上面的代码中,实现Comparable接口后,需要指定一个类型,这个类型就是我们要比较的类型。
主要还得看compareTo方法,这个方法中,定义了比较规则,规则就是如果前一个对象的score比后一个对象的score大,那么就返回一个正数,如果两个对象的score相等,就返回一个0,如果后一个对象的score比前一个对象的score大,就返回一个负数,编译器正是利用这个规则来进行排序的。就是说我们目前的代码就是以根据每个对象的score来进行排序的。
如果是想要以score为基准,从大到小排序,只需要把compareTo中的this和o调换位置即可。
public int compareTo(Student o) {
return o.score-this.score ;
}
如果要是基于name字段进行排序。就需要把重新在compareTo中实现。
public int compareTo(Student o) {
return this.name.compareTo(o.name);
}
可以看出在compareTo中再次的调用了compareTo方法,但是需要注意的是,这个compareTo方法不是我们写的这个方法,而是String的compareTo方法。因为name是String类型的。
以下代码就是String类的compareTo方法。
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
以字典序进行比较,如果前一个的name在字典序中大于后一个的name,那么就返回一个正数,如果前一个的name在字典序中比后一个的name在字典序中小,就返回负数。如果两个name都遍历完之后,还是没有发现不一样的字符,就返回他们的长度差值。
可以看出,当我们重行在compareTo中定义规则之后,排序的时候,就会按照我们定义的规则进行排序。
如果此时Student类中有很多的字段,当我们业务变化的时候,就得更改把compareTo的比较规则。
Comparator接口
@Override
public int compare(Student o1, Student o2) {
return 0;
}
compare方法是有两个参数的,所以基于Comparator接口自定义一个比较器。
package Blog;
import java.util.Arrays;
import java.util.Comparator;
class Student {
public int score;
public String name;
public Student() {
}
public Student(int score, String name) {
this.score = score;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"score=" + score +
", name='" + name + '\'' +
'}';
}
}
/**
* 基于score的比较器
*/
class ScoreComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.score - o2.score;
}
}
/**
* 基于name的比较器
*/
class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
public class test {
public static void main(String[] args) {
Student student1 = new Student(90, "abc");
Student student2 = new Student(80, "bdc");
ScoreComparator scoreComparator = new ScoreComparator(); //实例化基于score比较器
//通过score比较器来比较两个对象的大小
System.out.println(scoreComparator.compare(student1,student2));
NameComparator nameComparator = new NameComparator(); //实例化基于name的比较器
//通过name比较器来比较两个对象的大小
System.out.println(nameComparator.compare(student1,student2));
}
}
可以看出还是和Comparable的compareTo方法的返回值一样。
我们通过自定义比较强的方式来定义对象的比较规则。
当前Student对象有两个属性,我们也就定义了两个比较规则,如果有多个属性的话,我们也可以定义多个比较规则,当我们想用那个属性进行比较的时候,就实例化对应属性的比较器,来进行比较。
我们常用到的Arrays.sort();方法也可以在在指定参数的时候,把我们写的比较器传入过去,在排序的时候就会以我们的传入的比较器的比较规则来进行排序。
package Blog;
import java.util.Arrays;
import java.util.Comparator;
class Student {
public int score;
public String name;
public Student() {
}
public Student(int score, String name) {
this.score = score;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"score=" + score +
", name='" + name + '\'' +
'}';
}
}
/**
* 基于score的比较器
*/
class ScoreComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.score - o2.score;
}
}
/**
* 基于name的比较器
*/
class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
public class test {
public static void main(String[] args) {
Student student1 = new Student(90, "abc");
Student student2 = new Student(80, "bdc");
ScoreComparator scoreComparator = new ScoreComparator(); //实例化基于score比较器
//通过score比较器来比较两个对象的大小
System.out.println(scoreComparator.compare(student1,student2));
NameComparator nameComparator = new NameComparator(); //实例化基于name的比较器
//通过name比较器来比较两个对象的大小
System.out.println(nameComparator.compare(student1,student2));
Student[] students ={student1,student2};
Arrays.sort(students,scoreComparator); //同时传入我们自定义的score比较器,排序时会以比较器的规则来排序
System.out.println(Arrays.toString(students));
Arrays.sort(students,nameComparator);//同时传入我们自定义的name比较器,排序时会以比较器的规则来排序
System.out.println(Arrays.toString(students));
}
}
第一个输出是以score的比较器来进行排序的。
第二个输出是以name的比较器来进行排序的。