【Java 学习】Comparable接口 和 Comparator接口,掌控排序逻辑解析,深入 Comparable 和 Comparator 的优雅切换
💬 欢迎讨论:如对文章内容有疑问或见解,欢迎在评论区留言,我需要您的帮助!
👍 点赞、收藏与分享:如果这篇文章对您有所帮助,请不吝点赞、收藏或分享,谢谢您的支持!
🚀 传播技术之美:期待您将这篇文章推荐给更多对需要学习Java语言、低代码开发感兴趣的朋友,让我们共同学习、成长!
1. Comparable接口
1.1 为什么要使用Comparable接口
先看代码两组代码:
代码1:
importjava.util.Arrays;publicclassMain{publicstaticvoidmain(String[] args){// 创建一个数组String[] strs ={"李华","小明","小红"};// 对数组进行排序Arrays.sort(strs);// 打印System.out.println(Arrays.toString(strs));}}上述代码可以打印出比较的后的顺序。
代码2:
importjava.util.Arrays;classStudent{publicString name;publicint age;publicStudent(String name,int age){this.name = name;this.age = age;}}publicclassMain{publicstaticvoidmain(String[] args){Student[] stus ={newStudent("小明",19),newStudent("小红",20),newStudent("小刚",18)};// 对数组进行排序Arrays.sort(stus);// 打印System.out.println(Arrays.toString(stus));}}上述的代码发生报错:

点击之后会跳转到如下图的代码:

代码如下:
privatestaticintcountRunAndMakeAscending(Object[] a,int lo,int hi){assert lo < hi;int runHi = lo +1;if(runHi == hi)return1;// Find end of run, and reverse range if descending// 解释:// a 是引用变量,此时是Student类型的引用变量// Comparable 强制把 a引用变量转化成comparable 类型,// 但是我们写的Student并没有实现接口Comparable ,所以发生报错if(((Comparable) a[runHi++]).compareTo(a[lo])<0){// Descendingwhile(runHi < hi &&((Comparable) a[runHi]).compareTo(a[runHi -1])<0) runHi++;reverseRange(a, lo, runHi);}else{// Ascendingwhile(runHi < hi &&((Comparable) a[runHi]).compareTo(a[runHi -1])>=0) runHi++;}return runHi - lo;}在源代码中,使用Array.sort()会把引用类型强制转化成Comparable
(Comparable) a[runHi++] 强制把 a引用变量转化成Comparable 类型,但是我们写的Student并没有实现接口Comparable ,所以发生报错
那么,String是不是实现了 Comparable接口呢?
看一看String的源代码:

那为什么都必须实现这个接口呢?
在讲解接口的时已经说过,接口是一个标准,大家都执行这个标准,设计出的程序才能通用。
如:String 实现了Comparable接口,它可以用Arrays.sort()进行快速排序。
1.2 Comparable 接口的语法
特点:
(1)用于定义默认的排序规则。
(2)实现了 Comparable 的类对象可以直接通过 Collections.sort() 或 Arrays.sort() 方法进行排序。
(3)它属于对象本身的一部分,默认定义了对象之间的比较逻辑。
Comparable 接口中只有一个方法:
intcompareTo(T o);方法参数与返回值:
(1)参数:T o,表示要比较的对象。
(2)返回值:
-----------返回负数:当前对象小于传入对象。
-----------返回零:当前对象等于传入对象。
-----------返回正数:当前对象大于传入对象。
语法:
publicclass 类名 implementsComparable<类名>{@OverridepublicintcompareTo(参数){//...return 返回值;}}示例:
// Comparable<> 什么类就需要填什么publicclassStudentimplementsComparable<Student>{privateString name;privateint score;publicStudent(String name,int score){this.name = name;this.score = score;}// Comparable 接口只有此一个方法,实现 compareTo 方法@OverridepublicintcompareTo(Student other){returnthis.score - other.score;// 按照分数升序排序// return other.score - this.score 按照分数降序排序}}1.3 Comparable 的使用
使用compareTo函数:
importjava.util.Arrays;classStudentimplementsComparable<Student>{publicString name;publicint age;publicStudent(String name,int age){this.name = name;this.age = age;}@OverridepublicintcompareTo(Student s){returnthis.age - s.age;}}publicclassMain{publicstaticvoidmain(String[] args){Student s1 =newStudent("小明",19);Student s2 =newStudent("小红",20);System.out.println(s1.compareTo(s2));}}返回的是19 - 20 的值:

使用Ayyars.sort()按照年龄升序排序:
importjava.util.Arrays;classStudentimplementsComparable<Student>{publicString name;publicint age;publicStudent(String name,int age){this.name = name;this.age = age;}@OverridepublicintcompareTo(Student s){returnthis.age - s.age;// 升序排序}@OverridepublicStringtoString(){return"Student{"+"name='"+ name +'\''+", age="+ age +'}';}}publicclassMain{publicstaticvoidmain(String[] args){Student[] stus ={newStudent("小明",19),newStudent("小红",20),newStudent("小刚",18)};// 排序前System.out.println("排序前:"+Arrays.toString(stus));// 对数组进行排序Arrays.sort(stus);// 排序后System.out.println("排序后"+Arrays.toString(stus));}}
Arrays.sort()排序使用Student中的compareTo函数了吗?我们怎么知道呢?
在compareTo中添加一个加打印语句:
@OverridepublicintcompareTo(Student s){System.out.println("comppareTo()");returnthis.age - s.age;}再次运行,结果:

很明显,使用Arrays.sort()时调用了compareTo函数。
2. Comparator 接口
2.1 为什么要有 Comparator 接口
一个有年龄排序方法的类:
classStudentimplementsComparable<Student>{publicString name;publicint age;publicint score;publicStudent(String name,int age,int score){this.name = name;this.age = age;this.score = score;}@OverridepublicintcompareTo(Student s){returnthis.age - s.age;// 升序排序}@OverridepublicStringtoString(){return"Student{"+"name='"+ name +'\''+", age="+ age +'}';}}Comarable 接口只能被重写一次,只能进行年龄比较,但是,我们要想进行排序的是分数score, 怎么办呢?
此时,Comarator接口可以解决这个问题。Comparator有很强的灵活性,可以设计比较任意的属性。
2.2 Comparator 接口的语法
特点:
(1)用于定义自定义的排序规则。
(2)它是一个独立的比较器,与对象本身的定义无关。
(3)当需要对同一类的对象应用多种排序规则时,Comparator 更加灵活。
(4)Comparator 的对象可以传递给 Collections.sort() 或 Arrays.sort() 方法。
Comparator 接口中有两个常用方法:
intcompare(T o1,T o2);booleanequals(Object obj)//判断是否与另一个比较器相等(通常很少需要重写)语法:
publicclass 类名 implementsComparator<需要比较类的类>{@Overridepublicintcompare(参数){//...return 返回值;}}2.3 Comparator 接口的使用
- 比较两个对象的成绩:
Studnet类:
publicclassStudent{publicString name;publicint age;publicint score;publicStudent(String name,int age,int score){this.name = name;this.age = age;this.score = score;}@OverridepublicStringtoString(){return"Student{"+"name='"+ name +'\''+", age="+ age +'}';}}比较类:
importjava.util.Comparator;// 注意,这里的类需要填进行比较的类classScoreSortimplementsComparator<Student>{// 重写比较函数// 注意,这里的参数有两个@Overridepublicintcompare(Student s1,Student s2){return s1.score - s2.score;}}两个对象的score进行比较:
publicclassMain{publicstaticvoidmain(String[] args){// 创建两个对象Student s1 =newStudent("小明",19,80);Student s2 =newStudent("小红",20,70);// 创建一个排序对象ScoreSort ss =newScoreSort();// 打印出比较的结果System.out.println(ss.compare(s1,s2));}}结果:

- 比较一个数组的成绩:
Arrays.sort()可以填入两个参数,只需要把Arrays.sort(array,object)中的object换成排序对象即可。
publicclassMain{publicstaticvoidmain(String[] args){Student[] stus ={newStudent("小明",19,80),newStudent("小红",20,70),newStudent("小刚",18,90)};// 排序前System.out.println("排序前:"+Arrays.toString(stus));// 创建排序的对象ScoreSort scoreSort =newScoreSort();// 对数组进行排序Arrays.sort(stus,scoreSort);//需要添加排序对象// 排序后System.out.println("排序后"+Arrays.toString(stus));}}结果:

3 Comparable 和 Comparator 的比较
Comparable 和 Comparator 的核心区别
| 特性 | Comparable | Comparator |
|---|---|---|
| 用途 | 定义对象的默认排序规则 | 定义自定义的排序规则 |
| 位置 | 排序逻辑在对象内部实现 | 排序逻辑在外部定义 |
| 接口方法 | compareTo(T o) | compare(T o1, T o2) |
| 实现方式 | 对象类实现 Comparable 接口 | 通过实现 Comparator 接口创建比较器 |
| 灵活性 | 只能定义一种排序规则 | 可以定义多种排序规则 |
| 使用场景 | 对象有一个自然排序 | 对象需要多种排序规则或自定义排序 |
| 修改类代码的需求 | 必须修改类的代码以实现接口 | 不需要修改类的代码,可在外部定义排序逻辑 |
选择 Comparable 还是 Comparator?
| 使用场景 | 选择 |
|---|---|
| 类的排序逻辑是固定的,且只有一种排序方式。 | Comparable |
| 类的排序逻辑可能有多种(如按年龄、按名字)。 | Comparator |
| 类的代码无法修改(如第三方库类)。 | Comparator |
| 需要更灵活的排序方式。 | Comparator |