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

Java 中的 hashCode 和 equals 方法之间有什么关系?

Java 中 hashCode 和 equals 方法的关系

1. 核心契约关系

hashCode()equals()之间存在强契约关系,这是 Java 规范中的硬性要求:

契约规则

如果 a.equals(b) 返回 true 那么 a.hashCode() == b.hashCode() 必须为 true

反之不成立

如果 a.hashCode() == b.hashCode() a.equals(b) 不一定为 true(哈希冲突)

2. 为什么需要这种关系?

哈希表的工作原理

HashMap 结构示意: ┌─────────────────────────────────────┐ │ Bucket 0: [Entry1] → [Entry2] │ ← hashCode() 决定放入哪个桶 │ Bucket 1: [Entry3] │ │ Bucket 2: [Entry4] → [Entry5] │ │ ... │ └─────────────────────────────────────┘

查找流程

  1. 先用hashCode()快速定位到桶
  2. 再用equals()在桶内精确匹配

3. 违反契约的后果

示例:只重写 equals,不重写 hashCode

publicclassBadPerson{privateStringname;@Overridepublicbooleanequals(Objectobj){if(this==obj)returntrue;if(obj==null||getClass()!=obj.getClass())returnfalse;BadPersonperson=(BadPerson)obj;returnObjects.equals(name,person.name);}// ❌ 没有重写 hashCode(),使用 Object 的默认实现}// 问题演示BadPersonp1=newBadPerson("张三");BadPersonp2=newBadPerson("张三");System.out.println(p1.equals(p2));// trueSystem.out.println(p1.hashCode()==p2.hashCode());// false!违反契约// 在 HashMap 中出问题Map<BadPerson,String>map=newHashMap<>();map.put(p1,"值1");System.out.println(map.get(p2));// null!找不到

原因

  • p1p2hashCode()不同(基于内存地址)
  • 放入不同的桶中
  • p2查找时,去错误的桶中查找,找不到p1

4. 正确实现示例

publicclassGoodPerson{privateStringname;privateintage;@Overridepublicbooleanequals(Objectobj){if(this==obj)returntrue;if(obj==null||getClass()!=obj.getClass())returnfalse;GoodPersonperson=(GoodPerson)obj;returnage==person.age&&Objects.equals(name,person.name);}@OverridepublicinthashCode(){// ✅ 使用相同的字段计算 hashCodereturnObjects.hash(name,age);}}// 正确工作GoodPersonp1=newGoodPerson("张三",25);GoodPersonp2=newGoodPerson("张三",25);System.out.println(p1.equals(p2));// trueSystem.out.println(p1.hashCode()==p2.hashCode());// true ✅Map<GoodPerson,String>map=newHashMap<>();map.put(p1,"值1");System.out.println(map.get(p2));// "值1" ✅ 正确找到

5. 实现原则

原则 1:一致性

// equals 和 hashCode 必须使用相同的字段@Overridepublicbooleanequals(Objectobj){returnObjects.equals(name,((Person)obj).name)&&age==((Person)obj).age;}@OverridepublicinthashCode(){// ✅ 使用相同的字段:name 和 agereturnObjects.hash(name,age);}

原则 2:不可变字段优先

publicclassUser{privatefinalStringid;// 不可变,适合用于 hashCodeprivateStringname;// 可变,不建议用于 hashCode@OverridepublicinthashCode(){// ✅ 只用不可变字段returnObjects.hash(id);}}

原则 3:性能考虑

@OverridepublicinthashCode(){// ✅ 计算快速且分布均匀intresult=17;result=31*result+(name!=null?name.hashCode():0);result=31*result+age;returnresult;}

6. 常见错误

错误 1:只重写其中一个

// ❌ 错误:只重写 equals@Overridepublicbooleanequals(Objectobj){...}// 没有重写 hashCode// ❌ 错误:只重写 hashCode@OverridepublicinthashCode(){...}// 没有重写 equals

错误 2:使用不同字段

@Overridepublicbooleanequals(Objectobj){returnname.equals(((Person)obj).name);// 只用 name}@OverridepublicinthashCode(){returnObjects.hash(name,age);// ❌ 用了 name 和 age}

错误 3:在 hashCode 中使用可变字段

publicclassMutableKey{privateintvalue;@OverridepublicinthashCode(){returnvalue;// ❌ 危险!修改 value 后无法从 Map 中找到}}MutableKeykey=newMutableKey();key.setValue(10);map.put(key,"value");key.setValue(20);// 修改了 keymap.get(key);// null!hashCode 变了,找不到

7. IDE 自动生成

现代 IDE 可以自动生成符合规范的代码:

// IntelliJ IDEA / Eclipse 生成@Overridepublicbooleanequals(Objecto){if(this==o)returntrue;if(o==null||getClass()!=o.getClass())returnfalse;Personperson=(Person)o;returnage==person.age&&Objects.equals(name,person.name);}@OverridepublicinthashCode(){returnObjects.hash(name,age);}

8. 总结

场景equalshashCode结果
两个对象相等true相等✅ 正确
两个对象不等false可能相等✅ 允许(哈希冲突)
两个对象不等false不等✅ 正常
equals 为 truetrue不等❌ 违反契约

记住

  • 重写equals()必须重写hashCode()
  • 两者使用相同的字段
  • 优先使用不可变字段
  • 使用 IDE 自动生成避免错误

这种契约关系是 Java 集合框架正确工作的基础!

http://www.jsqmd.com/news/394870/

相关文章:

  • g2o中信息矩阵(Information Matrix)的理解
  • 如何在大数据领域使用Hive进行数据可视化
  • 什么是 Java 中的动态代理?
  • Java 中 hashCode 和 equals 方法是什么?它们与 == 操作符有什么区别?
  • 《计算机是怎样跑起来的》————让写算法跟呼吸一样简单
  • 购物卡回收的三种热门方法整理 - 京回收小程序
  • ChatPPT Nano Banana Pro · Magic模式深度解析 ——重新定义“所想即所得”的PPT智能编辑
  • ARM Cortex-A7(IMX6ULL)嵌入式裸机开发指南:从点灯到中断 - 实践
  • 大润发购物卡回收靠谱的3个主流渠道 - 京回收小程序
  • 天猫超市购物卡回收常见三种方法及流程解析 - 京回收小程序
  • 最近在调试西门子808D数控系统的机械手刀库,整个过程虽然有点复杂,但还挺有意思的。今天就来分享一下我的调试经验,顺便贴点代码,希望能帮到有需要的朋友
  • 镜像孪生驱动的视频孪生升级版水利电力三维态势控制中枢白皮书---依托矩阵视频融合架构、统一三维坐标基准构建技术、动态误差修正模型与风险交汇时间解算算法形成的空间级前向布控平台-
  • 2026年公司起名机构推荐榜单:十大专业品牌深度测评,企业选型必看 - 博客万
  • 视频孪生之上,是镜像孪生镜像视界三维空间控制作战体系---基于镜像视界(浙江)科技有限公司矩阵视频融合、Pixel-to-3D 反演引擎、三维轨迹建模体系与趋势级风险推演算法构建的全域主动压制平
  • 从春晚舞台到万家灯火:菁彩Vivid三度携手央视频,以沉浸体验点亮中国年 - 博客万
  • 6大方法禁止win11自动更新
  • 进口维生素d3十大品牌揭晓,维生素d3哪个牌子成分安全?复配K2,锁钙护血管更安心 - 博客万
  • 免费招聘平台TOP榜盘点,前三名免费查看简历 - 博客万
  • 目前最靠谱的招聘网站?2026权威测评与真实口碑 - 博客万
  • 【Docker高级篇】吃透Docker CI/CD集成:从代码提交到镜像部署,一步到位不踩坑
  • 【Docker高级篇】吃透容器编排:Swarm vs K8s 核心差异,为后续K8s学习打牢基础
  • 【Docker高级篇】新手也能懂的应用安全:为什么不能用root?镜像怎么扫漏洞?
  • 大数据架构数据流水线:从采集到分析的完整设计
  • 基于博途1200PLC + HMI病床呼叫控制系统仿真探索
  • A2UI协议,打破Agent交互壁垒,让智能系统自主“搭建”界面 - 指南
  • YOLO26涨点改进 | 独家创新,注意力改进篇 | AAAI 2025 | YOLO26引入 DRM 防御优化模块,进行特征优化/特征增强,助力目标检测、图像分类、图像分割有效涨点
  • dokuwikiAPI 探索器
  • 哨兵机制(sentinel)的原理
  • Redis客观下线
  • 一文搞懂超详细ubuntu22.04部署k8s1.28高可用(一)【多master+keepalived+nginx实现负载均衡】:核心原理+实战案例