【Java 学习】Comparable接口 和 Comparator接口,掌控排序逻辑解析,深入 Comparable 和 Comparator 的优雅切换

【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 接口的使用

  1. 比较两个对象的成绩:

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));}}

结果:

在这里插入图片描述
  1. 比较一个数组的成绩:

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 的比较

ComparableComparator 的核心区别

特性ComparableComparator
用途定义对象的默认排序规则定义自定义的排序规则
位置排序逻辑在对象内部实现排序逻辑在外部定义
接口方法compareTo(T o)compare(T o1, T o2)
实现方式对象类实现 Comparable 接口通过实现 Comparator 接口创建比较器
灵活性只能定义一种排序规则可以定义多种排序规则
使用场景对象有一个自然排序对象需要多种排序规则或自定义排序
修改类代码的需求必须修改类的代码以实现接口不需要修改类的代码,可在外部定义排序逻辑

选择 Comparable 还是 Comparator

使用场景选择
类的排序逻辑是固定的,且只有一种排序方式。Comparable
类的排序逻辑可能有多种(如按年龄、按名字)。Comparator
类的代码无法修改(如第三方库类)。Comparator
需要更灵活的排序方式。Comparator

Read more

C++之模版详解(进阶)

C++之模版详解(进阶)

目录 1. 非类型模板参数 2. 类模板的特化 2.1 函数模板特化 2.2 类模版特化 3. 模板的分离编译 1. 非类型模板参数 模版参数有两种,一种叫类型模版参数,一种叫做非类型模版参数。今天我们来讲讲非类型模版参数。 template <int N> 中的 int N 就是典型的非类型模板参数。这里的 int 是参数的类型,而 N 是参数名,它接收的是一个具体的常量值,而非像普通类型模板参数(如 template <typename T>)那样接收一个 “类型”。 两者核心区别就是: * 类型模板参数:传递 “类型”(如 T

By Ne0inhk
【问题反馈】JNI 开发:为什么 C++ 在 Debug 正常,Release 却返回 NaN?

【问题反馈】JNI 开发:为什么 C++ 在 Debug 正常,Release 却返回 NaN?

摘要: 在 Android NDK / JNI 开发中,经常会遇到这样一种“诡异”问题:Debug 模式下运行完全正常,而 Release 模式却出现 NaN、Infinity 甚至随机结果。 本文通过一次真实的 JNI 坐标转换案例,深入分析了该问题的根本原因——C++ 返回局部栈内存指针所导致的未定义行为(Undefined Behavior)。 【问题反馈】JNI 开发:为什么 C++ 在 Debug 正常,Release 却返回 NaN? 本文为以下问题的解决记录。由于问题较为典型,故梳理备忘。 https://github.com/eqgis/Sceneform-EQR/discussions/16 一、问题现象描述 1. 现象

By Ne0inhk
C++学习之旅【C++伸展树介绍以及红黑树的实现】

C++学习之旅【C++伸展树介绍以及红黑树的实现】

🔥承渊政道:个人主页 ❄️个人专栏: 《C语言基础语法知识》《数据结构与算法》 《C++知识内容》《Linux系统知识》 ✨逆境不吐心中苦,顺境不忘来时路!🎬 博主简介: 引言:前篇文章,小编已经介绍了关于C++AVL树的实现!相信大家应该有所收获!接下来我将带领大家继续深入学习C++的相关内容!本篇文章着重介绍关于C++伸展树介绍以及红黑树的实现!伸展树与红黑树是两类极具代表性的BBST,且在工程实践中各有不可替代的价值:伸展树摒弃了"严格平衡”的执念,通过“伸展”操作将最近访问的节点移至根节点,利用“局部性原理”优化频繁访问的场景,实现均摊O(logn)的时间复杂度,适合缓存、热点数据查询等场景;红黑树则通过给节点着色并遵守严格的颜色规则,确保树的最长路径不超过最短路径的两倍,以 “弱平衡” 换稳定的最坏O(logn)性能,是C++ STL 中 std::map、std:

By Ne0inhk
个人整理的超全C++ 八股文(全是干货)

个人整理的超全C++ 八股文(全是干货)

目录 C++ 面向对象和面向过程 面向过程 面向对象 三大特性? C语言和C++的区别? C++编译过程 多态 是什么? 分类? 虚函数 是什么? 底层? 解决的问题? 构造函数不能设置为虚函数? 重载 重写 隐藏 引用 是什么? 好处 为什么不能初始化为空? 引用与指针的区别? 内存分区 堆和栈的区别? 指针常量和常量指针 NULL在C语言中是(void *)0在C++中是0? C++用nullptr代指空指针? 构造函数 是什么? 拷贝构造 调用时机 拷贝构造参数不是引用行吗? 深浅拷贝的区别? 析构函数 是什么? 内存分配和销毁用什么? new和malloc 区别? new delete malloc free?

By Ne0inhk