【Java】 对象的比较【比较器】

登神长阶

第七阶 Java对象的比较 


🎷一.Java对象的比较

🪗1.基于引用的比较

        基于引用的比较在Java中使用==运算符进行。它主要检查两个对象是否引用内存中的相同位置。以下是基于引用的比较的详细介绍:

  • 使用==运算符:
    • ==运算符用于比较两个对象的引用是否指向内存中的相同地址。
    • 如果两个对象的引用指向同一内存地址,则它们被认为是相等的。
    • 但如果两个对象虽然内容相同但是不同的实例(即指向不同的内存地址),那么它们将不相等。
    • 基本类型的对象可以直接比较大小。

基于引用的比较主要关注对象在内存中的位置,而不考虑其内容。这意味着即使两个对象的内容完全相同,只要它们不是同一个实例,基于引用的比较也会将它们视为不相等。

基本数据类型 

public class TestCompare {
        public static void main(String[] args) {
            int a = 10;
            int b = 20;
            System.out.println(a > b); //false
            System.out.println(a < b); //true
            System.out.println(a == b); //false
            char c1 = 'A';
            char c2 = 'B';
            System.out.println(c1 > c2); //false
            System.out.println(c1 < c2); //true
            System.out.println(c1 == c2); //false
            boolean b1 = true;
            boolean b2 = false;
            System.out.println(b1 == b2); //false
            System.out.println(b1 != b2); //true
        }
    }

引用类型

 class Card {
        public int rank; // 数值
        public String suit; // 花色
        public Card(int rank, String suit) {
            this.rank = rank;
            this.suit = suit;
        }
    }
    public class TestPriorityQueue {
        public static void main(String[] args) {
            Card c1 = new Card(1, "♠");
            Card c2 = new Card(2, "♠");
            Card c3 = c1;
    //System.out.println(c1 > c2); // 编译报错
            System.out.println(c1 == c2); // 编译成功 ----> 打印false,
                                            因为c1和c2指向的是不同对象
    //System.out.println(c1 < c2); // 编译报错
            System.out.println(c1 == c3); // 编译成功 ----> 打印true,
                                            因为c1和c3指向的是同一个对象
        }
    }
        从编译结果可以看出,Java 中引用类型的变量不能直接按照 > 或者 < 方式进行比较 。 那为什么又可以使用 ==去 比较?
对于用户实现自定义类型,都默认继承自 Object 类,而 Object 类中提供了 equal 方法,而 == 默认情况下调 用的就是 equal 方法, 但是该方法的比较规则是: 没有比较引用变量引用对象的内容,而是直接比较引用变量的地 ,但有些情况下该种比较就不符合题意。
// Object中equal的实现,可以看到:直接比较的是两个引用变量的地址
public boolean equals(Object obj) {
    return (this == obj);
}

🎸2.基于对象的比较 

基于对象的比较在Java中是通过比较对象的内容而不是简单地比较对象的引用。这种比较通常通过重写equals()方法来实现。以下是基于对象的比较的详细介绍:

  • 使用equals()方法:

    • equals()方法是用于比较两个对象的内容是否相等的常用方法。
    • 大多数Java类都会覆盖equals()方法以便进行内容比较。
    • 默认情况下,equals()方法比较的是两个对象的引用,但可以通过重写该方法来自定义比较的行为。
    • 重写equals()方法时应当遵循一些约定,如对称性、传递性、一致性和非空性。
  • 重写equals()方法的注意事项:

    • 对称性:如果a.equals(b)为true,则b.equals(a)也应为true。
    • 传递性:如果a.equals(b)b.equals(c)均为true,则a.equals(c)也应为true。
    • 一致性:对于相同的对象重复调用equals()方法应始终返回相同的结果。
    • 非空性:对象不能为null。即调用a.equals(null)时应返回false。

基于对象的比较允许开发者根据对象的内容来确定它们是否相等,而不仅仅是根据它们在内存中的位置。这使得在处理复杂数据结构或需要考虑对象状态的情况下,能够更精确地比较对象。

例如:

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

🎹二.基于Comparble接口类的比较

ComparbleJDK提供的泛型的比较接口类,源码实现具体如下:

public interface Comparable<E> {
    // 返回值:
    // < 0: 表示 this 指向的对象小于 o 指向的对象
    // == 0: 表示 this 指向的对象等于 o 指向的对象
    // > 0: 表示 this 指向的对象大于 o 指向的对象
    int compareTo(E o);
}

        在Java中,实现了Comparable接口的类可以使用compareTo()方法进行对象比较。这种比较方式允许对象自身定义比较逻辑,使得它们可以按照特定的顺序进行排序。以下是基于Comparable接口的对象比较的详细介绍:

  • Comparable接口:

    • Comparable接口位于java.lang包中,其中定义了一个方法compareTo()
    • 实现了Comparable接口的类可以使用compareTo()方法来比较对象。
    • compareTo()方法返回一个整数值,用于表示两个对象之间的大小关系。
    • 如果当前对象小于另一个对象,则返回负数;如果两个对象相等,则返回零;如果当前对象大于另一个对象,则返回正数。
  • 实现Comparable接口的步骤:

    1. 类声明时实现Comparable接口,并指定泛型类型为当前类。
    2. 在类中实现compareTo()方法,定义对象之间的比较逻辑。
  • 示例:

public class Card implements Comparable<Card> {
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
    // 根据数值比较,不管花色
    // 这里我们认为 null 是最小的
    @Override

    public int compareTo(Card o) {
        if (o == null) {
            return 1;
        }
        return rank - o.rank;
    }

    public static void main(String[] args){
        Card p = new Card(1, "♠");
        Card q = new Card(2, "♠");
        Card o = new Card(1, "♠");
        System.out.println(p.compareTo(o)); // == 0,表示牌相等
        System.out.println(p.compareTo(q)); // < 0,表示 p 比较小
        System.out.println(q.compareTo(p)); // > 0,表示 q 比较大
    }
}

在这个示例中,Card类实现了Comparable接口,并重写了compareTo()方法

通过实现Comparable接口,类可以定义自己的比较逻辑,从而实现对象之间的自定义排序。

🎻三.基于比较器的比较

除了实现Comparable接口外,还可以使用比较器(Comparator)来进行对象比较。比较器允许在不修改对象本身的情况下定义多种比较规则。以下是基于比较器的对象比较的详细介绍:

  • Comparator接口:

    • Comparator接口定义了用于比较两个对象的方法compare()
    • 通过实现Comparator接口,可以创建自定义的比较器,从而对对象进行比较。
    • 比较器可以在不修改对象本身的情况下定义多种比较规则,使得同一类型的对象可以根据不同的需求进行排序。
  • 使用Comparator的步骤:

    1. 创建一个实现了Comparator接口的类。
    2. 实现compare()方法,定义对象之间的比较逻辑。
  • 示例:

    import java.util.Comparator;
    class Card {
        public int rank; // 数值
        public String suit; // 花色
        public Card(int rank, String suit) {
            this.rank = rank;
            this.suit = suit;
        }
    }
    class CardComparator implements Comparator<Card> {
        // 根据数值比较,不管花色
        // 这里我们认为 null 是最小的
        @Override
    
        public int compare(Card o1, Card o2) {
            if (o1 == o2) {
                return 0;
            }
            if (o1 == null) {
                return -1;
            }
            if (o2 == null) {
                return 1;
            }
            return o1.rank - o2.rank;
        }
    
        public static void main(String[] args){
            Card p = new Card(1, "♠");
            Card q = new Card(2, "♠");
            Card o = new Card(1, "♠");
    // 定义比较器对象
            CardComparator cmptor = new CardComparator();
    // 使用比较器对象进行比较
            System.out.println(cmptor.compare(p, o)); // == 0,表示牌相等
            System.out.println(cmptor.compare(p, q)); // < 0,表示 p 比较小
            System.out.println(cmptor.compare(q, p)); // > 0,表示 q 比较大
        }
    }
    
    

    🪫四.以上三种比较的分析

比较方式基于比较器比较基于Comparable接口类的比较基于对象的比较
主要接口/类Comparator接口和实现了它的比较器类Comparable接口和实现了它的类equals()方法
实现方式创建实现了Comparator接口的比较器类,并实现compare()方法实现Comparable接口的类,并实现compareTo()方法重写类的equals()方法
比较规则的灵活性非常高,可以根据需要定义多种不同的比较规则较高,但仅限于定义类本身的比较规则一般,取决于重写equals()方法的程度
是否修改类的定义不需要修改类的定义,可以在外部定义不同的比较规则不需要修改类的定义,但需要在类内实现compareTo()方法需要在类中重写equals()方法
对象的排序方式可以在排序时指定不同的比较器,从而实现不同的排序方式对象可以直接通过Collections.sort()方法进行排序,按照类内定义的比较逻辑无法直接排序对象,但可以通过ComparatorComparable进行间接排序
  • 基于比较器比较提供了更大的灵活性,允许在不修改类定义的情况下定义多种比较规则,适用于对外部库的类进行比较或需要多种不同比较规则的情况。
  • 基于Comparable接口类的比较允许类自身定义对象之间的比较规则,但对类的修改较多。基于对象的比较适用于直接对类的内容进行比较,但排序需要借助其他手段。

覆写方法的类比 

覆写的方法
说明
Object.equals
因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与否
Comparable.compareTo
需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序
Comparator.compare
需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性强

🔎五.集合框架中PriorityQueue的比较方式

集合框架中的PriorityQueue底层使用堆结构,因此其内部的元素必须要能够比大小,它根据元素的优先级进行排序。在 PriorityQueue 中,采用了: Comparble和Comparator两种方式。

  1. Comparble是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparble接口,并覆写compareTo方法
  2. 用户也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口并覆写compare方法。
使用 PriorityQueue 创建大小堆,解决 TOPK 问题
//使用比较器创建小根堆
class LessIntComp implements Comparator<Integer>{
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2;
    }
}

//使用比较器创建大根堆
class GreaterIntComp implements Comparator<Integer>{
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
}

public class TestDemo<E> {
    //求最小的K个数,通过比较器创建大根堆
    public static int[] smallestK(int[] array, int k) {
        if(k <= 0) {
            return new int[k];
        }
        GreaterIntComp greaterCmp = new GreaterIntComp();
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(greaterCmp);
//先将前K个元素,创建大根堆
        for(int i = 0; i < k; i++) {
            maxHeap.offer(array[i]);
        }
//从第K+1个元素开始,每次和堆顶元素比较
        for (int i = k; i < array.length; i++) {
            int top = maxHeap.peek();
            if(array[i] < top) {
                maxHeap.poll();
                maxHeap.offer(array[i]);
            }
        }
//取出前K个
        int[] ret = new int[k];
        for (int i = 0; i < k; i++) {
            int val = maxHeap.poll();
            ret[i] = val;
        }
        return ret;
    }
    public static void main(String[] args) {
        int[] array = {4,1,9,2,8,0,7,3,6,5};
        int[] ret = smallestK(array,3);
        System.out.println(Arrays.toString(ret));
    }
}

🪘六.总结与反思

要找出时间来考虑一下,一天中做了什么,是正号还是负号。——季米特洛夫

        在Java中,对象之间的比较是开发中经常遇到的任务之一。有三种主要的比较方式:基于比较器比较、基于Comparable接口类的比较以及基于对象的比较(通常是通过重写equals()方法)。它们分别适用于不同的场景和需求:

  • 基于比较器比较:

    • 实现了Comparator接口的比较器类,可以定义多种不同的比较规则。
    • 适用于对外部库的类进行比较或需要多种不同比较规则的情况。
  • 基于Comparable接口类的比较:

    • 实现了Comparable接口的类,可以在类内部定义对象之间的比较规则。
    • 适用于对类自身的比较逻辑进行定义,但可能需要修改类的定义。
  • 基于对象的比较:

    • 通过重写类的equals()方法来实现对象之间的比较。
    • 适用于直接对类的内容进行比较,但排序需要借助其他手段。

        总的来说,基于比较器比较提供了最大的灵活性,允许在不修改类定义的情况下定义多种比较规则。而基于Comparable接口类的比较允许类自身定义对象之间的比较规则,但对类的修改较多。基于对象的比较适用于直接对类的内容进行比较,但排序需要借助其他手段。在实际开发中,根据具体的需求和情况选择合适的比较方式可以提高代码的可读性和灵活性。


🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/588320.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Qt QML】Frame组件

Frame&#xff08;框架&#xff09;包含在&#xff1a; import QtQuick.Controls继承自Pane控件。用于在可视框架内布局一组逻辑控件。简单来说就是用来包裹和突出显示其他可视元素。Frame不提供自己的布局&#xff0c;但需要自己对元素位置进行设置和定位&#xff0c;例如通过…

vue3与js的router基本使用方式

title: vue3与js的router基本使用方式 tags: vue3js abbrlink: ‘57270957’ date: 2024-04-17 18:54:47 第一步快捷引入的别名 使用路由需要大量在src文件中引用所需要的地址&#xff0c;并且组件中也需要很多的包的引用&#xff0c;将快速跳转到src这一文件的步骤进行简化操…

如何从 iPhone 恢复已删除或丢失的联系人?

不小心删除了您的 iPhone 联系人&#xff1f;不用担心。我们将向您展示如何从 iPhone或 iPad恢复已删除或丢失的联系人。当您从 iPhone 中删除联系人时&#xff0c;您可能认为无法将其恢复。但事实是&#xff0c;您可以从 iPhone 或 iPad 恢复已删除的联系人&#xff0c;因为它…

模型智能体开发之metagpt-多智能体实践

参考&#xff1a; metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case&#xff0c;但是现实生活场景是很复杂的&#xff0c;所以单智能体远远不能满足我们的诉求&#xff0c;所以仍然还需要了解多智能体的实现。通过多个role对动…

手撕spring框架(3)

手撕spring框架&#xff08;3&#xff09; 相关系列 手撕spring框架&#xff08;1&#xff09; 手撕spring框架&#xff08;2&#xff09; InitializingBean 接口详解 什么是 InitializingBean 接口&#xff1f; InitializingBean 接口是 Spring 框架中的一个接口&#xff0c…

【linux】进程(深入理解linux进程状态)

开始之前先说一个与本文无关的小知识&#xff0c;chdir命令可以更改当前进程的工作目录哦。 目录 linux具体进程状态&#xff1a;R && S&#xff1a;T && t&#xff1a;D&#xff1a;僵尸进程 && 孤儿进程&#xff1a; OS的理论线&#xff1a;运行&…

模型训练中的过拟合和欠拟合

基本概念 我们知道&#xff0c;所谓的神经网络其实就是一个复杂的非线性函数&#xff0c;网络越深&#xff0c;这个函数就越复杂&#xff0c;相应的表达能力也就越强&#xff0c;神经网络的训练则是一个拟合的过程。   当模型的复杂度小于真实数据的复杂度&#xff0c;模型表…

正版Office-Word使用时却提示无网络连接请检查你的网络设置 然后重试

这是购买电脑时自带的已经安装好的word。看纸箱外壳有office标记&#xff0c;但是好像没有印系列号。 某天要使用。提示&#xff1a;无网络连接请检查你的网络设置。 经过网上高手的提示&#xff1a; 说要勾选勾选ssl3.0、TLS1.0、1.1、1.2。 我的截图 我电脑进去就缺1.2. …

2024五一数学建模A题思路代码与论文分析

2024五一数学建模A题完整代码和成品论文获取↓↓↓↓↓ https://www.yuque.com/u42168770/qv6z0d/gyoz9ou5upvkv6nx?singleDoc# 2024五一数学建模A题钢板最优切割路径问题需要建立的模型和算法: 图论 最短路径算法(Dijkstra算法、Floyd算法等) 动态规划 网格化离散建模 …

Surya:强大的开源 OCR 文字识别工具

在当今数字化时代&#xff0c;文字识别技术扮演着至关重要的角色。VikParuchuri/surya 便是一款令人瞩目的开源 OCR 文字识别工具。 主要功能&#xff1a; 支持 90 多种语言的文字识别&#xff1a;Surya 具备强大的语言兼容性&#xff0c;能够轻松应对多种语言的文字识别任务&…

保存钉钉群直播回放下载:直播回放下载步骤详解

今天&#xff0c;我们就来拨开云雾&#xff0c;揭开保存钉钉群直播回放的神秘面纱。教会你们如何下载钉钉群直播回放 首先用到的工具我全部打包好了&#xff0c;有需要的自己下载一下 钉钉群直播回放工具下载&#xff1a;https://pan.baidu.com/s/1WVMNGoKcTwR_NDpvFP2O2A?p…

基于EBAZ4205矿板的图像处理:03摄像头采集HDMI输出视频图像

基于EBAZ4205矿板的图像处理&#xff1a;03摄像头采集HDMI输出视频图像 先看效果 项目简介 我是使用的EBAZ4205矿板&#xff0c;超级大电工的转接板和我自己买的一块没有xclk的ov5640完成的该项目&#xff0c;没有设备需自备。我就是跑通了正点原子的开源代码&#xff08;下文…

1991-2022年上市公司短贷长投/短债长用/投融资期限错配(包含原始数据及Stata代码)

01、数据简介 上市公司在投融资过程中&#xff0c;可能会涉及到投融资期限错配、短债长用和短贷长投等问题 投融资期限错配是指企业的资产与债务期限不匹配&#xff0c;主要表现为“短存长贷”&#xff0c;即资金来源短期化、资金运用长期化。当风险缓释的期限比当前的风险暴…

Elasticsearch:理解近似最近邻 (ANN) 算法

作者&#xff1a;来自 Elastic Elastic Platform Team 如果你是在互联网出现之前长大的&#xff0c;你会记得找到新喜好并不总是那么容易。我们是在无意中听到收音机里的新乐队时发现他们的&#xff0c;是因为忘了换频道偶然看到一个新电视节目的&#xff0c;也是几乎完全依据游…

本地搭建llama大模型及对话UI

环境说明&#xff1a;MBP 2023 M2Pro芯片 用到的工具/组件/技术&#xff1a;ollama、llama3:8b、docker、open-webui 1.下载ollama ollama官网下载地址&#xff1a;https://ollama.com/download 到ollama官网地址下载对应操作系统版本的ollama平台&#xff0c;按照安装指引…

unity制作app(2)--主界面

1.先跳转过来&#xff0c;做一个空壳&#xff01;新增场景main为4号场景&#xff01; 2.登录成功跳转到四号场景&#xff01; 2.在main场景中新建canvas&#xff0c;不同的状态计划用不同的panel来设计&#xff01; 增加canvas和底图image 3.突然输不出来中文了&#xff0c;浪…

区块链 | IPFS:CID

&#x1f98a;原文&#xff1a;Anatomy of a CID &#x1f98a;写在前面&#xff1a;本文属于搬运博客&#xff0c;自己留存学习。 1 CID 在分布式网络中与其他节点交换数据时&#xff0c;我们依赖于内容寻址&#xff08;而不是中心化网络的位置寻址&#xff09;来安全地定位…

stm32单片机开发四、USART

串口的空闲状态时高电平&#xff0c;起始位是低电平&#xff0c;来打破空闲状态的高电平 必须要有停止位&#xff0c;停止位一般为一位高电平 串口常说的数据为8N1&#xff0c;其实就是8个数据位&#xff08;固定的&#xff09;&#xff0c;N就是none&#xff0c;也就是0个校验…

审计师能力与专长数据集(2014-2022年)

01、数据介绍 审计师是专门从事审计工作的人员&#xff0c;他们对企业、政府机关、金融机构等组织进行独立的、客观的、合法的审计&#xff0c;以评估这些组织的财务状况、经营绩效和风险水平。审计师通过收集和评估证据&#xff0c;以确定被审计单位的财务报表是否公允、合法…

[数据结构]———交换排序

目录 1.交换排序 第一个定义了一个名为Swap的函数 第二个三数取中 2.冒泡排序 代码解析 冒泡排序的特性总结&#xff1a; 3.快速排序 1. hoare版本 2. 挖坑法 代码解析 3. 前后指针版本 代码解析 1.交换排序 基本思想&#xff1a;所谓交换&#xff0c;就是根据序列中两…
最新文章