当前位置: 首页 > news >正文

数据结构 Java对象的比较

在Java中,凡是涉及到比较的,可以分为两类情况:一类是基本数据类型的比较,另一类是引用数据类型的比较。对于基本数据类型的比较,我们通过关系运算符(==、>、<、!=、>=、<=)进行它们之间的比较,而对于引用数据类型,并不能简单的通过关系运算符来进行它们的比较,因为引用数据类型如果使用关系运算符比较的话,比较的是它们的引用类型,并不是对象的内容。

本文就引用数据类型如何进行比较展开讨论。

1.对象比较存在的问题

来看下面的例子:

class Card {public int rank;  //点数public String suit;  //花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}
}public class Test {public static void main(String[] args) {Card card1 = new Card(1,"方块");Card card2 = new Card(2,"方块");Card card3 = card1;System.out.println(card1 > card2);   //1.System.out.println(card1 == card2);  //2.System.out.println(card2 < card1);   //3.System.out.println(card1 == card3);  //4.}
}

对于代码中的1.、2.、3.和4.语句,1.和3.不能通过编译,2.和4.可以通过遍历,并且运行结果分别是:false(因为card1和card2是不同的对象)、true(card1和card3是相同的对象)。

面对这个结果,我们易产生疑问:为什么Java中引用类型的变量不能直接按照<或者>进行比较,为什么==就可以呢

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

但是有些情况下,我们需要比较的是引用变量引用对象的内容,比如:向优先级队列中插入某个对象时,需要比较对象的内容来进行调整堆,那这又该怎么办呢?

2.对象比较的方式

对于上述所说的需要比较对象内容的情况,再根据具体的比较需要,有三种方案,它们分别是:

重写父类的equals方法实现Comparable接口使用比较器Comparator接口

2.1 重写父类的equals方法

该方案的核心用途:判断两个对象的内容是否 “逻辑相等”,是最基础的对象内容比较方式。

适用场景:一般的对象内容比较(如判断两个Person是否为同一个人)

在上述的例子中,我们我们认为两张牌的点数和花色一样,就认为它们是同一张牌,那么重写equals方法,再进行测试:

package demo1;import java.util.Objects;class Card {public int rank;  //点数public String suit;  //花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}@Overridepublic boolean equals(Object o) {//自己和自己比较if (this == o) {return true;}//如果是null对象或者o不是不是Card的子类if (o == null || getClass() != o.getClass()) {return false;}Card card = (Card) o;//比较点数和花色(花色是String类型,因此调用String的equals方法进行比较)return rank == card.rank && Objects.equals(suit, card.suit);}}public class Test {public static void main(String[] args) {Card card1 = new Card(1,"方块");Card card2 = new Card(1,"方块");Card card3 = card1;System.out.println(card1.equals(card2));System.out.println(card1.equals(card3));}
}//运行结果
true
true

代码解读:

  1. 如果指向同一个对象,返回 true
  2. 如果传入的为 null,返回 false
  3. 如果传入的对象类型不是 Card,返回 false
  4. 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌
  5. 注意下调用其他引用类型的比较也需要 equals,例如这里的 suit 的比较

注意:重写父类equal的方式虽然可以比较,但缺陷是:equal只能按照相等进行比较,不能按照大于、小于的方式进行比较,也就是说只能比较两个东西是不是同一个。

2.2 实现Comparable接口

该方案的核心用途:为类定义 “默认排序规则”,让对象自身具备可比较性。

适用场景:对象有明确的 “自然顺序”(如学生按学号排序、商品按价格排序),且排序规则相对固定。

Comparable是JDK提供的泛型的比较接口类,它的源码如下:

public interface Comparable<E> {

        // 返回值:

        // < 0: 表示 this 指向的对象小于 o 指向的对象

         // == 0: 表示 this 指向的对象等于 o 指向的对象

         // > 0: 表示 this 指向的对象大于 o 指向的对象

}

对于我们用户自定义类型,如果想要按照大小进行比较时:在定义类时,实现Comparable接口即可,然后在类中重写CompareTo方法。像这样:

class Card implements Comparable<Card>{public int rank;  //点数public String suit;  //花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}//不在意花色,进行点数的比较@Overridepublic int compareTo(Card o) {if (o == null) {return 1;}return rank - o.rank;}
}
public class Test {public static void main(String[] args) {Card card1 = new Card(2,"梅花");Card card2 = new Card(3,"方块");Card card3 = new Card(2,"方块");System.out.println(card1.compareTo(card2));  // < 0,说明card1小于card2System.out.println(card1.compareTo(card3));  //== 0,说明card1等于card3System.out.println(card2.compareTo(card3));  // > 0,说明card2大于card3}
}//运行结果
-1
0
1

【注意】Comparable是Java.lang中的接口类,可以直接使用。

2.3 使用比较器Comparator接口

该方案的核心用途:为类定义 “临时 / 额外的排序规则”,不修改原类代码。

适用场景

  • 需要多种排序规则(如Person既可以按年龄排,也可以按姓名排);
  • 无法修改原类代码(如第三方库的类);
  • 优先级队列(PriorityQueue)的元素排序(建大根堆需要)。

按照比较器方式进行比较,具体的操作如下:

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

public interface Comparator {

        // 返回值:

        // < 0: 表示 o1 指向的对象小于 o2 指向的对象

        // == 0: 表示 o1 指向的对象等于 o2 指向的对象

        // > 0: 表示 o1 指向的对象等于 o2 指向的对象

        int compare(T o1, T o2);

}

2.重写Comparator中的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> {//重写compare方法//依旧按照数值比较@Overridepublic int compare(Card o1, Card o2) {//如果两张牌数值一样if (o1.rank == o2.rank) {return 0;}//如果第一种牌为nullif (o1 == null) {return -1;}//如果第二张牌为nullif (o2 == null) {return 1;}//正常情况return o1.rank - o2.rank;}
}
public class Test {public static void main(String[] args) {Card card1 = new Card(1,"方块");Card card2 = new Card(2,"方块");Card card3 = new Card(1,"方块");//定义比较器对象CardComparator cardComparator = new CardComparator();System.out.println(cardComparator.compare(card1,card3)); // == 0,说明两张牌相等System.out.println(cardComparator.compare(card1,card2)); // < 0,说明card1小于card2System.out.println(cardComparator.compare(card2,card1)); // > 0,说明card2大于card1}
}//运行结果
0
-1
1

【注意】Comparator是java.util 包中的泛型接口类,使用时必须导入对应的包。

2.4 三种方式的对比

重写的方法说明
Object.equals因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与 否
Comparable.compareTo

需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序

Comparator.compare需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性 强

到此,关于如何去比较引用数据类型变量这个问题已经得到解决,我们有三种方案,按需使用即可!感谢您的阅读,如有错误,还请指出!谢谢!


文章转载自:

http://RJs5zO6E.jcjgh.cn
http://B7hQV1dT.jcjgh.cn
http://kkLeudfH.jcjgh.cn
http://vIlNtWTa.jcjgh.cn
http://tQMDp3xi.jcjgh.cn
http://jGIky3XN.jcjgh.cn
http://k6gs7OE2.jcjgh.cn
http://EWXR4vr0.jcjgh.cn
http://r7ybqluV.jcjgh.cn
http://sUQCKbsy.jcjgh.cn
http://32lzFkwd.jcjgh.cn
http://hZLuMGsQ.jcjgh.cn
http://bX1OelRI.jcjgh.cn
http://6EbC8Dpw.jcjgh.cn
http://HWNJFeS5.jcjgh.cn
http://r3EqV13Y.jcjgh.cn
http://ElAzyJNb.jcjgh.cn
http://RXXerBeQ.jcjgh.cn
http://RZUEjNXi.jcjgh.cn
http://7e1nxQED.jcjgh.cn
http://k7ob5CAD.jcjgh.cn
http://RAC5RRJB.jcjgh.cn
http://lTPASFf5.jcjgh.cn
http://b0rrKFTS.jcjgh.cn
http://8VdxYSNL.jcjgh.cn
http://VeWBqJSf.jcjgh.cn
http://mA9FfIEJ.jcjgh.cn
http://CtTkyK5y.jcjgh.cn
http://PH1oLe0M.jcjgh.cn
http://UknQhReb.jcjgh.cn
http://www.dtcms.com/a/379665.html

相关文章:

  • EDID 数据结构解析与编辑工具:校验和计算、厂商/设备名编解码、物理地址读写、颜色与时序信息提取
  • 龙蜥8.10中spark各种集群及单机模式的搭建spark3.5.6(基于hadoop3.3.6集群)
  • Hadoop MapOutputBuffer:Map高性能核心揭秘
  • Kubernetes 弹性伸缩:深入讲解 HPA 和 VPA
  • 代理服务器是什么?怎么选择?
  • java Redisson 实现限流每秒/分钟/小时限制N个请求 -V2.0
  • 高并发、低延迟全球直播系统架构
  • zookeeper是啥
  • 短波红外相机在机器视觉检测方向的应用
  • 阿里云国际代理:如何利用RDS构建高可用、可扩展的数据库架构
  • 【Python】通俗理解反向传播
  • RFID技术在半导体电子货架上的应用方案
  • Windows 安装 Redis 教程
  • CMake 全流程开发实战:从零开始掌握C++项目构建、测试到一键分发的完整解决方案​
  • 如果数据量小但是点击后需要获取的是最新的定位信息,这种时候采取什么策略最优?
  • 使用 Pyinstaller 打包 PPOCRLabel
  • 科技信息差(9.12)
  • 是德科技 | 关于AI 数据中心时代的光通信的精选问答
  • 深入剖析 Elasticsearch (ES) 的近实时搜索原理
  • Qt5 | TCP服务器开源模板工程实战
  • 飞鹤财报“新解”:科技筑牢护城河,寒冬凸显龙头“硬核力”
  • 第6.2节 Android Agent开发<一>
  • 【 C/C++ 算法】入门动态规划-----一维动态规划基础(以练代学式)
  • YOLOv8 从yaml配置文件生成PyTorch模型
  • 重复文件清理的标准化操作流程
  • Amazon DocumentDB Serverless 技术深度解析:架构特性、弹性扩缩容机制与实操指南
  • 项目管理方法适合什么类型的企业
  • HTTPS(Hypertext Transfer Protocol Secure,超文本传输安全协议)
  • 【LLM越狱】AI大模型DRA攻击解读与复现
  • k8s下的发布策略详解