夯实Java基础(十五)——Java中Comparable和Comparator
1、前言
对于Java中的对象,我们只能使用基本运算符==、!=来判断一下地址是否相等,不能使用>、<来比较大小。但是在实际的开发中,我们需要对对象进行排序,也就是比较大小,那么应该如何实现呢?这就涉及到了Java中的两个常用的接口Comparable<T>和Comparator<T>的实现了。下面通过示例学习这两个接口的使用。
2、Comparable接口
Comparable是一个基于排序接口,它是自然排序。该接口中只有一个compareTo(Obj o)方法,用于给一个类的多个实例比较大小,进而完成排序。也就是说某个类实现了Comparable接口,就意味着该类支持排序。通过实现类重写compareTo(Obj o)方法,从而规定多个实例的自然顺序,然后使用Arrays.sort 或Collections.sort 方法对数组对象或List对象进行排序。
用String举一个简单的例子:
public class CompareTest {
public static void main(String[] args) {
String[] str=new String[]{"AA","EE","DD","CC","FF","BB"};
Arrays.sort(str);
System.out.println(Arrays.toString(str));
}
}
运行结果为:

可以发现,在使用Arrays.sort(str)之后就完成了排序,但是我们并没有调用compareTo(Obj o)方法。我们知道String源码中实现了Comparable接口并且重写该接口的方法,在使用Arrays.sort(str)时sort方法内部间接的调用了String的compareTo(Obj o)方法,所以我们直接就看到了排序的结果。像String、包装类等实现了Comparable接口,重写了compareTo方法,都清晰的给出了比较两个对象的方式,可以自行去看一下String重写的compareTo方法的源码。但是在重写compareTo(Obj o)方法时都需要遵循这三个规则:
①、如果比较者(即this当前对象)大于被比较者(即compareTo方法里面的形参),则返回正整数。
②、如果比较者小于被比较者,那么返回负整数。
③、如果比较者等于被比较者,那么返回零。
自定义的类是无法排序的,但是通过实现Comparable接口之后就可以实现,然后通过Arrays.sort 或Collections.sort 方法排序。我们来看一个自己定义的类怎么使用Comparable接口进行排序:
public class Person implements Comparable{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//按名字排序
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person p= (Person) o;
//name是String类型,这里直接调用String的compareTo
if (this.name.compareTo(p.name)>0){
return 1;
}else if(this.name.compareTo(p.name)<0){
return -1;
}else{
return 0;
}
}else{
throw new RuntimeException("传入数据类型不一致...");
}
}
public static void main(String[] args) {
Person[] p=new Person[5];
p[0]=new Person("Jack",23);
p[1]=new Person("Marry",13);
p[2]=new Person("Tom",18);
p[3]=new Person("John",33);
p[4]=new Person("Thomas",41);
System.out.println("排序前------------");
for (Person person : p) {
System.out.print(person.getName()+":"+person.getAge()+"\n");
}
System.out.println("排序后------------");
Arrays.sort(p);
for (Person person : p) {
System.out.print(person.getName()+":"+person.getAge()+"\n");
}
}
}
运行结果为:

在Person类中实现了Comparable接口并且重写compareTo(Obj o)方法,然后我们按照名字排序,可以发现它默认的排序方式是升序,如果要降序则可以在返回值前面加一个负号。
3、Comparator接口
Comparator也是一个排序接口,它和Comparable功能是一样的,但是它是定制排序。怎么来理解定制排序呢?如果某个类没有实现Comparable接口,而该类本身是不支持排序的,那么我们就可以使用Comparator来进行排序,或者我们自定义类实现了Comparable接口后,但是自定义类的代码不能再更改了,这时需要改变comparaTo(Obj o)方法中排序的方式,此时也可以选择定制排序Comparator。Comparator接口中有一个compare(T o1, T o2)方法和compareTo(Obj o)类似,定义排序的规则一样:
o1>o2,返回值为正整数。
o1<o2,返回值为负整数。
o1=o2,返回值为零。
同样使用String简单举例:
public class CompareTest {
public static void main(String[] args) {
String[] str=new String[]{"AA","EE","DD","CC","FF","BB"};
//使用匿名内部类直接创建
Arrays.sort(str, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1.compareTo(o2)>0){
return 1;
}else if (o1.compareTo(o2)<0){
return -1;
}else{
return 0;
}
}
});
System.out.println(Arrays.toString(str));
}
}
我们知道接口是不能被实例化的,这里是匿名内部类的知识,可以自行去度娘寻找答案。
自定义类使用Comparator进行排序:
public class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Person[] p=new Person[5];
p[0]=new Person("Jack",23);
p[1]=new Person("Marry",13);
p[2]=new Person("Tom",18);
p[3]=new Person("John",33);
p[4]=new Person("Thomas",41);
System.out.println("排序前------------");
for (Person person : p) {
System.out.print(person.getName()+":"+person.getAge()+"\n");
}
System.out.println("排序后------------");
Arrays.sort(p, new Comparator<Person>() {
//按照年龄默认排序,如果年龄相同则按照名字默认排序
@Override
public int compare(Person o1, Person o2) {
if (o1 instanceof Person && o2 instanceof Person) {
if (o1.age > o2.age) {
return 1;
}else if (o1.age<o2.age){
return -1;
}else {
return o1.name.compareTo(o2.name);
}
}else{
throw new RuntimeException("传入数据类型不一致...");
}
}
});
for (Person person : p) {
System.out.print(person.getName()+":"+person.getAge()+"\n");
}
}
}
程序运行结果:

这样就使用Comparator定制排好序了。
4、小结
小结一下Comparable和Comparator的使用和区别:
①、Comparable是java.lang包下的,而Comparator是java.util包下的。
②、Comparable可以看做是内部比较器,而Comparator是外部比较器。
③、Comparable是自然排序,Comparator是定制排序。
④、如果某个类没有实现Comparable接口,而该类本身是不支持排序的,那么我们就可以使用Comparator来进行定制排序。
⑤、或者我们自定义类实现了Comparable接口后,但是自定义类的代码不能再更改了,这时需要改变compareTo(Obj o)方法中排序的方式,此时也可以选择定制排序Comparator。
这两种方法各有优劣:某个类实现了Comparable接口后,在任何地方都可以比较大小,但是有时候需要修改其比较的方式,则需要修原有的代码。而用Comparator的好处就是不需要修改原有代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。
夯实Java基础(十五)——Java中Comparable和Comparator的更多相关文章
- java基础(十五)----- Java 最全异常详解 ——Java高级开发必须懂的
本文将详解java中的异常和异常处理机制 异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常. Java异常的分类和类结构图 1.Java中的所 ...
- java基础(十二 )-----Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...
- Java进阶(十五)Java中设置session的详细解释
Java中设置session的详细解释 简单通俗的讲session就是象一个临时的容器,用来存放临时的东西.从你登陆开始就保存在session里,当然你可以自己设置它的有效时间和页面,举个简单的例子: ...
- java基础(十八)----- java动态代理原理源码解析
关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 静态代理 1.静态代理 静态代理:由程序员创建或特定工 ...
- Java实习生常规技术面试题每日十题Java基础(五)
目录 1.启动一个线程是用run()还是start()? . 2.线程的基本状态以及状态之间的关系. 3.Set和List的区别,List和Map的区别? 4.同步方法.同步代码块区别? 5.描述Ja ...
- “全栈2019”Java第九十五章:方法中可以定义静态局部内部类吗?
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第二十五章:流程控制语句中循环语句while
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第十五章:Unicode与转义字符
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Java进阶(三十五)java int与integer的区别
Java进阶(三十五)java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象 ...
- Java进阶(二十五)Java连接mysql数据库(底层实现)
Java进阶(二十五)Java连接mysql数据库(底层实现) 前言 很长时间没有系统的使用java做项目了.现在需要使用java完成一个实验,其中涉及到java连接数据库.让自己来写,记忆中已无从搜 ...
随机推荐
- make工具简介
在Linux C/C++的开发过程中,当源代码文件较少时,我们可以手动使用gcc或g++进行编译链接,但是当源代码文件较多且依赖变得复杂时,我们就需要一种简单好用的工具来帮助我们管理.于是,make应 ...
- 2020年python学习进阶方向
相信很多友人在学习python过程都会遇到很多 虽然python入门很容易 但是难免会遇到瓶颈 遇到问题没人交流 很难提升 对此 给你们简单指点学习方向 1.认识python linux基本 ...
- MySQL - Schema和Database的区别
问题来源 在pycharm发现Create new schema的效果和新建数据库一样,所以产生这个问题 参考 https://stackoverflow.com/questions/11618277 ...
- 5_2 木块问题(UVa101)<vector的使用>
[背景] 在计算机科学中的很多地方都会使用简单,抽象的方法来做分析和实验验究.比如在早期的规划学和机器人学的人工智能研究就利用一个积木世界,让机械臂执行操作积木的任务. 在这个问题中,你将在确定的规则 ...
- 【代码审计】VAuditDemo 命令注入漏洞
一般PHP中可以使用下列函数来执行外部的应用程序或命令 system() exec() passthru() shell_exec() 跟踪$cmd --> 跟进$target,发现传递给tar ...
- IPv4地址被用光,IPv6将接手
截止2019年11月26号,全球所有43亿个IPv4地址已全部分配完毕,这一情况也宣告着IPv6时代的正式来临.IPv6和5G一样是关系到国家安全和战略发展的重大事情. IPv6简单来说,就是一个互联 ...
- CN109241772A发票区块链记录方法、装置、区块链网关服务器和介质(腾讯)
学习笔记-2 CN109241772A发票区块链记录方法.装置.区块链网关服务器和介质(腾讯) 解决什么问题? 让发票信息记录到区块链的情况下减少发票信息泄露 链上有什么数据? 发行发票事件信息(发票 ...
- Python版GPA计算器
最近在网申投简历时遇到一个需要计算GPA的问题,想起自己在上学时写的Excel公式版GPA计算器略显low,而且操作也比较复杂,于是一时兴起,写了个Python版的,在此分享给大家,也算是跟上时代的浪 ...
- 使用 OClint 进行静态代码分析
OCLint 就是一个建立在 Clang 上的工具,能够发现代码中潜在的问题. 最近需要一个静态分析代码工具,帮助我们发布运行应用前找到代码潜在的问题. 其实对于iOS开发,我们的日常开发上已经用到了 ...
- mcast_block_source函数
#include <errno.h> #include <sys/socket.h> #define SA struct sockaddr int mcast_block_so ...