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

java之hashCode() 方法和 equals(Object obj) 方法之间的关系

1、 hashCode() 方法和 equals(Object obj)

在Java中,hashCode() 方法和 equals(Object obj) 方法之间的关系是紧密相连的,特别是在使用基于哈希的集合(如 HashSetHashMapHashTable 等)时。这两个方法共同决定了对象在哈希表中的存储和检索行为。

hashCode() 方法

  • hashCode() 方法用于获取对象的哈希码。
  • 哈希码是一个整数,由对象的内部地址或根据对象的字段通过某种算法计算得到。
  • 哈希码的主要目的是在哈希表中减少碰撞(即不同的对象具有相同的哈希码)的概率,从而提高查找效率。

equals(Object obj) 方法

  • equals(Object obj) 方法用于比较两个对象是否相等。
  • 在没有重写 equals 方法的情况下,equals 方法比较的是对象的引用(即内存地址)。
  • 重写 equals 方法时,通常也会重写 hashCode 方法,以保持它们之间的一致性关系。

hashCode() 和 equals(Object obj) 之间的关系

  • 一致性(Consistency):对于任何非null的引用值x和y,当且仅当 x.equals(y)true 时,x.hashCode() 必须等于 y.hashCode()
  • 不同对象可以有相同的哈希码:两个不相等的对象(即 equals(Object obj) 返回 false)可以有相同的哈希码。
  • 如果两个对象相等:根据 equals(Object obj) 方法的定义,如果两个对象相等(即 equals(Object obj) 返回 true),那么对这两个对象中的每一个调用 hashCode() 方法都必须产生相同的整数结果。

为什么需要保持一致性

在基于哈希的集合中,当你尝试添加、查找或删除一个对象时,集合首先会计算该对象的哈希码,以决定它在哈希表中的哪个桶(bucket)中。然后,它会在该桶中遍历所有对象,使用 equals() 方法来查找确切的对象。

如果 hashCode() 方法没有与 equals() 方法保持一致,那么即使两个对象通过 equals() 方法被认为是相等的,它们也可能被放置在哈希表的不同桶中,导致无法正确找到对象,或者导致哈希表无法正确管理对象的存储和检索。

因此,当你重写 equals() 方法时,通常也需要重写 hashCode() 方法,以确保这两个方法之间的一致性。

2、不同对象可以有相同的哈希码的原因

在哈希表中,不同对象可以有相同的哈希码(也称为哈希冲突或哈希碰撞)的原因主要归结为哈希函数的有限性和对象的无限性之间的矛盾。

  1. 哈希函数的有限性:哈希函数的设计目标是将任意长度的输入(比如对象的状态或关键属性)映射到固定长度的输出(即哈希码,通常是整数)。由于输出空间是固定的,因此哈希函数只能产生有限数量的不同哈希码。

  2. 对象的无限性:在理论上,可以创建的对象数量是无限的,因为对象的属性可以有无限多种组合方式。即使只考虑有限的几个属性,这些属性的不同组合也会导致理论上无限多种不同的对象。

  3. 概率性:由于哈希函数的输出空间有限,而输入空间(即可能的对象集合)无限,因此必然存在多个不同的对象映射到同一个哈希码的情况。这是概率上的必然结果,尤其是在处理大量数据时。

  4. 性能与空间的权衡:哈希表的设计需要在性能(查找、插入和删除操作的平均时间复杂度)和空间(哈希表所需的内存)之间做出权衡。使用更复杂、输出范围更大的哈希函数可以减少哈希冲突,但会增加计算哈希码所需的时间和空间成本。相反,使用更简单、输出范围较小的哈希函数可以提高性能,但会增加哈希冲突的概率。

  5. 解决哈希冲突:为了处理哈希冲突,哈希表通常使用各种策略,如开放寻址法(open addressing)和链地址法(chaining,也称为分离链接法)。在链地址法中,每个桶(bucket)实际上是一个链表,所有具有相同哈希码的对象都被添加到同一个链表中。这样,即使不同对象具有相同的哈希码,它们也可以被正确地存储和检索。

因此,不同对象可以有相同的哈希码是哈希表设计中的一个固有特性,需要通过合理的哈希函数和冲突解决策略来管理。

3、介绍开放寻址法(open addressing)和链地址法(chaining,也称为分离链接法)

开放寻址法(Open Addressing)和链地址法(Chaining,也称为分离链接法)是两种解决哈希表中哈希冲突的主要方法。下面分别介绍这两种方法:

1. 开放寻址法(Open Addressing)

定义与原理

  • 开放寻址法是一种哈希表的冲突解决方法,它要求所有的元素都存储在哈希表的数组中。当发生冲突时,即两个不同的元素通过哈希函数计算得到的哈希值相同时,开放寻址法会寻找数组中的下一个空闲位置,直到找到一个空槽或遍历整个表为止。

实现方式

  • 线性探测(Linear Probing):当发生冲突时,依次检查下一个位置,直到找到一个空槽或者遍历整个表。公式为 hash(key, i) = (hash(key) + i) % tableSize
  • 二次探测(Quadratic Probing):根据一个二次方程的形式探测下一个位置,公式为 hash(key, i) = (hash(key) + c1 * i + c2 * i^2) % tableSize
  • 双重哈希(Double Hashing):使用两个哈希函数,第一个哈希函数计算出位置,如果发生冲突,则通过第二个哈希函数计算一个步长,再次寻找下一个位置。公式为 hash(key, i) = (hash1(key) + i * hash2(key)) % tableSize

优势与劣势

  • 优势:不需要额外的数据结构来存储相同哈希值的元素,节省空间。
  • 劣势:可能导致聚集(clustering)问题,即相邻的位置可能会被频繁占用,导致查找效率下降。同时,扩容和重新哈希的成本较高。

2. 链地址法(Chaining,分离链接法)

定义与原理

  • 链地址法是一种通过链表解决哈希冲突的方法。在哈希表的每个槽位上,不直接存储数据元素,而是存储一个指向链表的指针。所有映射到该槽位的数据元素都存储在链表中。

实现方式

  • 当哈希函数计算出的槽位已有元素时,将新元素添加到该槽位对应链表的末尾。
  • 查找、插入和删除操作都首先定位到槽位,然后在链表中进行。

优势与劣势

  • 优势:处理冲突简单,只需在链表末尾添加新元素。同时,链表的长度可以动态变化,适应不同数量的元素。
  • 劣势:在极端情况下,某些槽位的链表可能非常长,导致查找效率下降。此外,链表需要额外的空间来存储指针。

装填因子

  • 在链地址法中,装填因子α定义为哈希表中关键字记录总数N与哈希表大小M的比值,即α=N/M。它反映了哈希表的填充程度,对哈希表的性能有重要影响。

综上所述,开放寻址法和链地址法各有优缺点,选择哪种方法取决于具体的应用场景和需求。

http://www.lryc.cn/news/412549.html

相关文章:

  • 首届「中国可观测日」圆满落幕
  • [Docker][Docker NetWork][下]详细讲解
  • 安卓系统在未来如何更好地解决隐私保护与数据安全的问题?
  • MySQL innodb单表上限一般多少
  • 更小、更安全、更透明:Google发布的Gemma推动负责任AI的进步
  • 基于Django框架的医疗耗材管理系统的设计实现-计算机毕设定制-附项目源码(可白嫖)48999
  • 物联网协议篇(1):modbus tcp和modbusRTU的区别是什么?
  • JVM系列 | 对象的消亡——HotSpot的设计细节
  • vue 运行或打包过程报错 JavaScript heap out of memory(内存溢出)
  • git分支提交方法
  • 从微架构到向量化--CPU性能优化指北
  • 声声入耳,事事如意 爱可声「如意」助听器即将上市!
  • 生物实验室设备文件采集如何才能质量和效率双管齐下?
  • Framework源码整编、单编、烧录过程
  • TypeScript类型断言
  • Mallet:一款针对任意协议的安全拦截代理工具
  • 【IEEE出版】第五届大数据、人工智能与软件工程国际研讨会(ICBASE 2024,9月20-22)
  • 自修室预约小程序的设计
  • 用于跟踪个人图书馆的BookLogr
  • 深入解析JVM垃圾回收机制:Full GC、Minor GC与Major GC
  • Windows10点击文件夹右键卡死的解决办法
  • C# 设计模式之单例模式
  • 【组合数学】【Python】【小练习】一、斯特灵近似式求阶乘
  • 【IEEE Fellow特邀报告,JPCS独立出版】第四届电子通信与计算机科学技术国际学术会议(ECCST 2024,9月20-22)
  • DockerCompose部署示例
  • 【云原生】Helm来管理Kubernetes集群的详细使用方法与综合应用实战
  • 电源插头应该统一方向
  • 大学新生编程入门最佳攻略
  • MySQL 的binlog 、undolog 、redolog
  • 【计算机网络】三次握手、四次挥手