Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Comparable接口和Comparator接口

Comparable接口和Comparator接口

作者头像
VIBE
发布于 2022-12-02 08:18:59
发布于 2022-12-02 08:18:59
67000
代码可运行
举报
文章被收录于专栏:算法与开发算法与开发
运行总次数:0
代码可运行

CSDN话题挑战赛第2期 参赛话题:学习笔记

前言

博主在很早之前写过一篇面向对象编程的文章,其中粗略的介绍了一下Comparable接口的使用,现在问题来了,Comparabe接口和Comparator接口的异同点是什么呢?

一、元素的比较

1.1 基本类型的比较

Java中,基本类型的对象可以直接比较大小。

1.2 对象的比较

Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较。

那为什么==可以比较?

因为:对于用户实现自定义类型,都默认继承自Object类,而Object类中提供了equals方法,而==默认情况下调用的就是equals方法,但是该方法的比较规则是:没有比较引用变量引用对象的内容,而是直接比较引用变量的地址,但有些情况下该种比较就不符合题意。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Object中equal的实现,可以看到:直接比较的是两个引用变量的地址
public boolean equals(Object obj) {
return (this == obj);
}

二、引用对象的比较

有些情况下,需要比较的是对象中的内容,比如:向优先级队列中插入某个对象时,需要对按照对象中内容来调整堆,那该如何处理呢?

2.1 覆写基类的equals

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Card {
    public int rank; // 数值
    public String suit; // 花色

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    @Override
    public boolean equals(Object o) {
        // 自己和自己比较
        if (this == o) {
            return true;
        }
        // o如果是null对象,或者o不是Card的子类
        if (o == null || !(o instanceof Card)) {
            return false;
        }
        // 注意基本类型可以直接比较,但引用类型最好调用其equal方法
        Card c = (Card) o;
        return rank == c.rank && suit.equals(c.suit);
    }
}

注意: 一般覆写 equals 的套路就是上面演示的

  1. 如果指向同一个对象,返回 true
  2. 如果传入的为 null,返回 false
  3. 如果传入的对象类型不是 Card,返回 false
  4. 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌
  5. 注意下调用其他引用类型的比较也需要 equals,例如这里的 suit 的比较
  6. 覆写基类equal的方式虽然可以比较,但缺陷是:equal只能按照相等进行比较,不能按照大于、小于的方式进行比较。

2.2 Comparable接口

接下来将用一个例子介绍java.lang.Comparable接口:

给对象数组排序

给定一个学生类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Student {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
}

再给定一个学生对象数组, 对这个对象数组中的元素进行排序(按分数降序).

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Student[] students = new Student[] {
            new Student("张三", 95),
            new Student("李四", 96),
            new Student("王五", 97),
            new Student("赵六", 92),
    }

按照我们之前的理解, 数组工具类我们有一个现成的 sort 方法, 能否直接使用这个方法呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Arrays.sort(students);
System.out.println(Arrays.toString(students));
 
 // 运行出错, 抛出异常. 
Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to 
java.lang.Comparable    

仔细思考, 不难发现, 和普通的整数不一样, 两个整数是可以直接比较的, 大小关系明确. 而两个学生对象的大小关系怎么确定? 需要我们额外指定

让我们的 Student 类实现 Comparable 接口, 并实现其中的 compareTo 方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Student implements Comparable {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
    @Override
    public int compareTo(Object o) {
       if(this == 0){
       return 0; //返回0表示相等
       }
       if(o instanceof Student){
       //当前传入的o就是Student类型的引用,向下转型还原为Student
       //要比较Student对象的大小关系,就要用到Student的独有属性,向下转型
       Student stu = (Student)o;
       return this.score - stu.score;
       }
       //若传入不是学生类,则抛出异常
       throw new IllegalArgumentException("不是学生类型,无法比较!")
} 

在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.

然后比较当前对象和参数对象的大小关系(按分数来算).

  • 如果当前对象应排在参数对象之前, 返回小于 0 的数字;
  • 如果当前对象应排在参数对象之后, 返回大于 0 的数字;
  • 如果当前对象和参数对象不分先后, 返回 0;

再次执行程序, 结果就符合预期了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 执行结果
[[王五:97], [李四:96], [张三:95], [赵六:92]] 

注意事项: 对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备 compareTo 这样的能力. 通过重写 compareTo 方法的方式, 就可以定义比较规则。

为了进一步加深对接口的理解, 我们可以尝试自己实现一个 sort 方法来完成刚才的排序过程(使用冒泡排序):(其实Arrays.sort()内部也是和下面代码类似的,只是被封装了)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void sort(Comparable[] array) {
        for (int bound = 0; bound < array.length; bound++) {
            for (int cur = array.length - 1; cur > bound; cur--) {
                if (array[cur - 1].compareTo(array[cur]) > 0) {
                    // 说明顺序不符合要求, 交换两个变量的位置
                    Comparable tmp = array[cur - 1];
                    array[cur - 1] = array[cur];
                    array[cur] = tmp;
                }
            }
        }
    }

再次执行代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sort(students); 
System.out.println(Arrays.toString(students)); 
// 执行结果
[[王五:97], [李四:96], [张三:95], [赵六:92]] 

2.3 Comparator接口

用户自定义比较器类,实现Comparator接口:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Public interface Comparator<T> {
// 返回值:
// < 0: 表示 o1 指向的对象小于 o2 指向的对象
// == 0: 表示 o1 指向的对象等于 o2 指向的对象
// > 0: 表示 o1 指向的对象等于 o2 指向的对象
int compare(T o1, T o2);
}

假设现在覆写了Comparable接口的compareTo方法:

  • 如果需要得到一个升序数组 => this - o
  • 要得到一个降序数组,又得改代码 => o-this

假设如今业务需要要更改成逆序的,又得更改CompareTo方法。

而程序开发中遵循一个开闭原则:

一段程序应该对扩展开放,对修改关闭。 写好的代码别老改,要有新场景,就拓展新代码,不要影响老代码。

所以我们可以使用Comparator接口:

一个类若实现了这个接口,表示这个类天生就是为别的类的大小关系服务的:

StudentSec这个类天生就是为了Student对象的排序而存在

覆写compare方法(o1,o2) => int

表示o1和o2的大小关系: 返回值 >0 :o1> o2 返回值 =0 :o1 = o2 返回值<0 :o < o2

当把Student类的大小关系比较从Student类中“解耦”,此时的比较策略非常灵活,需要哪种方式,只需要创新一个新的类实现Comparator接口即可,根据此时大小关系的需要传入比较器的对象。(策略模式)

三、Comparable和Comparator的异同

3.1 共同点

  1. 这两个接口一般都是用来实现集合内的排序(如Collections.sort() 、Arrays.sort()、List.sort(Comparator))。

以下是Collections类下的sort方法,可以传入继承Comparable接口的泛型对象,也可以传入当前list 和 一个comparator比较器。

  1. 当调用集合排序方法的时候,如果直接传入list就会自行调用对象的compareTo()方法来实现对象的比较。如果传入list和coparator的话,就会调用compare()方法

3.2 区别

  1. Comparable接口位于java.lang包下,Comparator位于java.util包下
  2. Comparable接口只提供了一个compareTo()方法;Comparator接口不仅提供了compare()方法【两个参数】,还提供了其他默认方法,如reversed()、thenComparing(),使我们可以按照更多的方式进行排序
  3. Comparator相对于Comparable来说更加的灵活,耦合度低
  4. 如果要用Comparable接口,则必须实现这个接口,并重写compareTo()方法【一个参数】;但是Comparator接口可以在类外部使用,通过将该接口的一个匿名类对象当做参数传递给Collections.sort()方法或者Arrays.sort()方法实现排序。
  5. Comparator体现了一种策略模式,即可以不用要把比较方法嵌入到类中,而是可以单独在类外部使用,这样我们就可有不用改变类本身的代码而实现对类对象进行排序。

总结

用简单的话来讲就是:

  • Comparable接口可以让当前这个类拥有可以比较的能力,就可以和类型的对象进行比较
  • Comparator接口可以让我们定义不同的类,然后我们可以用这些自己定义好的排序类去对list中的对象按照某种规则去排序,不想用的时候可以换另一种排序类。(比如升序和降序)
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
《Java初阶数据结构》----8.<java对象的比较总结>
有些情况下,需要比较的是对象中的内容,比如:向优先级队列中插入某个对象时,需要对按照对象中内容来调整堆,那该如何处理呢?
用户11288958
2024/09/24
1350
《Java初阶数据结构》----8.<java对象的比较总结>
【IT领域新生必看】深入浅出Java:揭秘`Comparator`与`Comparable`的神奇区别
在Java编程中,对象排序是一个常见的需求。为了实现对象的排序,Java 提供了两个重要的接口:Comparable和Comparator。对于初学者来说,理解这两个接口的区别及其使用场景,是编写高效和灵活代码的关键一步。本篇文章将详细介绍Comparator与Comparable的定义、用法及其区别,帮助你全面理解这些重要概念。
E绵绵
2024/07/12
2000
Java中的比较器Comparable与Comparator
使用背景: 当元素没有实现java.lang.Comparable接口而又不方便改代码,或者是实现了Comparable接口,也指定了两个对象的比较大小的规则,但此时不想按照预定义的方法比较大小。
鱼找水需要时间
2023/04/28
7950
Comparable和Comparator
---- 1. Comparable接口 在java.lang包下,实现了Comparable函数式接口的对象可以自然排序,而数组和集合实现了该接口,所以我们会用Arrays.sort()或Collections.sort()来排序 Comparable比较大于就返回1,小于返回-1,等于返回0 如果自定义的对象也要排序,就需要实现该接口并且手动重写里面的compareTo()方法 返回值 函数名 解释 int compareTo(T o) 将此对象与指定的对象进行比较以进行排序 需要排序的自定义对
晚上没宵夜
2020/03/10
5210
【Java 学习】:抽象类&接口
💢💢在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
IsLand1314
2024/10/15
1680
【Java 学习】:抽象类&接口
《JAVA SE》面向对象编程(下篇)
上一篇讲到了接口,接下来将补充一下常用的接口以及Object类的初识,链接如下: 《JAVA SE》面向对象编程(中篇)
VIBE
2022/12/02
2810
Java 解惑:Comparable 和 Comparator 的区别
该文介绍了Java中的自然排序和比较器排序两种方式,并举例说明了使用这两种方式进行排序的具体实现。同时,也探讨了Comparator接口在定制排序中的实现和应用。
张拭心 shixinzhang
2018/01/05
2.1K0
Java 解惑:Comparable 和 Comparator 的区别
你所要知道的关于接口的知识点
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第31天,点击查看活动详情
用户9996207
2023/01/12
2530
Java的接口
推荐一个网站给想要了解或者学习人工智能知识的读者,这个网站里内容讲解通俗易懂且风趣幽默,对我帮助很大。我想与大家分享这个宝藏网站,请点击下方链接查看。 https://www.captainbed.cn/f1
鲜于言悠
2024/06/02
1130
Java的接口
抽象类与接口(3)(接口部分)
❤️❤️观察其内部结构我们可以知道在Comparable后面还有个<T>,在语法上这是泛型,之后会讲,这并不影响我们现在的思路,这个<T>中的T你写student类,后面的compareTo方法中的第一个参数就是student类,如上图。
E绵绵
2024/04/08
1000
抽象类与接口(3)(接口部分)
Java List排序:Comparable与Comparator接口及Stream API应用
在 Java 编程中,集合(List)元素排序是常见需求。本文将解读使用 Comparable 接口、Comparator 接口及 JDK 8 的 Stream API 对 List 进行高效排序,并通过实例代码演示用法和区别。
Yeats_Liao
2024/12/29
1280
Java List排序:Comparable与Comparator接口及Stream API应用
【数据结构】关于Java对象比较,以及优先级队列的大小堆创建你了解多少???
上期博客讲了优先级队列,优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较,为了简单起见,我们只是插入了Integer类型,那优先级队列中能否插入自定义类型对象呢?
用户11288949
2024/09/24
1050
【数据结构】关于Java对象比较,以及优先级队列的大小堆创建你了解多少???
Comparable和Comparator的区别和用法
(2)、自己写一个比较类class,实现Comparator接口并重写compare()方法
静谧星空TEL
2021/04/27
4360
Comparable和Comparator的区别和用法
java-comparator
要使自己的类拥有排序功能,就要实现comparator接口,重写compare方法。
luxuantao
2021/02/24
5410
Comparable 和 Comparator的理解
list或者数组实现了这个接口能够自动的进行排序,相关类的方法有Collections.sort(),Arrays.sort();
cxuan
2019/06/03
5920
Java.lang.Comparable接口和Java.util.Comparator接口的区别
Java的Comparator和Comparable当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序。 1.Comparable简介: Java.lang.Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。 此外,实现此接口的对象可以用作有序映射中的键
10JQKA
2018/05/09
1.1K0
Java.lang.Comparable接口和Java.util.Comparator接口的区别
java中Comparable和Comparator的区别
java.lang.Comparable和java.util.Comparator是两个容易混淆的接口,两者都带有比较的意思,那么两个接口到底有什么区别,分别在什么情况下使用呢?
子润先生
2021/06/22
3310
【day14】异常处理与Object类深入解析
在深入探讨异常处理与Object类之前,让我们回顾一下【day13】中的关键内容:
程序员波特
2024/12/25
1110
【day14】异常处理与Object类深入解析
【如何通过JAVA实现接口的应用和图书的管理】
我们可以通过查看String源码看到它里面有一个compareTo的方法,它能够帮助我们去比较引用类型的大小。
ImAileen
2024/12/17
880
【如何通过JAVA实现接口的应用和图书的管理】
似懂非懂的Comparable与Comparator
  Comparable与Comparator都是用于集合的排序,对于大多数人来说Comparator可能略微比Comparable要熟悉一点,类似下面这几句代码的使用频率应该是最高的。
用户1148394
2019/01/08
8360
相关推荐
《Java初阶数据结构》----8.<java对象的比较总结>
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验