新闻详情

新闻详情

首页 / 资讯中心 / 详情

重写了 equals,为什么 HashMap 还是找不到你要的对象

发布时间:2026/8/31 12:58:38来源:尧图网络
重写了 equals,为什么 HashMap 还是找不到你要的对象
「Java 进阶之路」系列 Day20写在前面这是一个几乎每个人都踩过的坑给一个类重写了equals自信满满地觉得两个属性一样的对象现在应该被判定为相等了结果一放进HashSetcontains查一个属性完全一样的新对象返回的却是false。问题不在equals写错了而是漏了另一半——hashCode。这篇就把这两个方法为什么必须绑在一起讲清楚。一、是什么一个判断相等一个决定放哪equals定义的是逻辑相等两个对象即使不是同一个引用只要关心的属性都一样就应该被判定为相等。Object类默认的equals比较的是引用地址等价于——不重写的话两个属性完全一样的不同对象永远不相等。hashCode返回一个int本意是给基于哈希表的容器HashMap、HashSet、Hashtable用来快速定位数据该放进哪个桶bucket。Object默认的hashCode是跟对象内存地址相关的一个整数同一个对象只要没被移动多次调用返回值不变但和equals判定的逻辑相等完全没关系。二、为什么HashMap 靠这两个方法配合才能定位到对象hashCode的官方契约里有一条硬性规定两个对象用equals判定相等hashCode就必须相等反过来不要求——hashCode相同的两个对象完全可以不相等这叫哈希冲突哈希表本来就要处理这种情况。这条契约不是凭空定的而是HashMap.get(key)的查找过程决定的先算key.hashCode()用这个值定位到某个桶再在这个桶里逐个用equals比较找到真正相等的那一个key对象计算hashCode定位到某个桶桶内可能有多个entry逐个用equals比较找到匹配返回没有匹配判定不存在如果只重写了equals、没重写hashCode两个逻辑相等的对象会因为默认的hashCode基于地址不同从一开始就被分到了不同的桶——equals再准确也没用压根不会走到逐个比较这一步。这就是为什么这两个方法必须成对重写equals负责终点的精确比较hashCode负责能不能先走到这个终点。三、怎么用三个容易踩的坑classPoint{intx,y;Point(intx,inty){this.xx;this.yy;}Overridepublicbooleanequals(Objecto){if(thiso)returntrue;if(!(oinstanceofPoint))returnfalse;Pointp(Point)o;returnxp.xyp.y;}// 没有重写 hashCode}SetPointpointsnewHashSet();points.add(newPoint(1,2));System.out.println(points.contains(newPoint(1,2)));// false逻辑相等的对象却查不到正确做法是让hashCode用和equals完全一致的字段来计算OverridepublicinthashCode(){returnObjects.hash(x,y);}Objects.hash内部就是把每个字段的hashCode按31做累乘累加IDE 自动生成、Lombok 的EqualsAndHashCode走的也是同一套思路。一个更隐蔽的坑用可变字段参与hashCode计算。对象放进HashSet之后如果再去修改参与hashCode计算的字段这个对象的hashCode会变但它在哈希表里的桶位置是插入那一刻就定死的、不会跟着重新调整——于是这个对象实际上迷失了后续用contains/remove都定位不到它原来所在的桶看起来就像对象凭空消失。参与equals/hashCode计算的字段最好是不可变的或者整个对象干脆设计成不可变对象。另一个容易忽略的坑类型检查该用instanceof还是getClass()。上面Point的equals用的是instanceof如果Point被继承出一个ColorPoint多了一个颜色字段instanceof会认为父类的Point(1, 2)和子类的ColorPoint(1, 2, 红色)只要坐标一样就相等——但反过来ColorPoint的equals通常会比较颜色判定两者不相等。这样就出现了p.equals(cp)为true、cp.equals(p)为false破坏了equals契约里的对称性a.equals(b)和b.equals(a)结果必须一致。改用o.getClass() ! this.getClass()判断类型能规避这个问题要求类型完全一致父子类之间永远不相等代价是丧失了父类引用可以和子类对象比较的灵活性——这也是为什么《Effective Java》建议优先用组合代替继承来复用带equals的类从设计上绕开这个两难。四、面试追问Q1为什么重写 equals 一定要重写 hashCode因为HashMap/HashSet这类基于哈希表的容器查找时先用hashCode()定位桶再在桶内用equals()精确比较。只重写equals不重写hashCode两个equals判定相等的对象会因为默认hashCode基于地址不同而被分到不同桶导致contains/get永远找不到本该相等的元素违反了equals相等则hashCode必须相等这条硬性契约。Q2hashCode 相等的两个对象equals 一定相等吗不一定。hashCode允许出现哈希冲突equals不相等的对象完全可以有相同的hashCode反过来equals相等则hashCode必须相等这是单向约束不能反过来推。Q3用可变字段参与 hashCode 计算会有什么风险对象放入哈希表之后如果修改了参与hashCode计算的字段对象的hashCode会变但它在哈希表里的桶位置是插入时就定死的不会跟着重新调整于是这个对象实际上丢失了——后续用get/contains/remove都找不到它。所以参与计算的字段最好是不可变的。Q4Object 类默认的 equals 和 hashCode 分别是什么行为默认equals比较的是引用地址等价于默认hashCode返回一个跟对象内存地址相关的整数同一个对象多次调用结果不变但和逻辑相等没有关系。不重写的话两个属性完全一样的不同对象永远被判定为不相等。Q5用 Lombok 的EqualsAndHashCode或 IDE 自动生成时要注意什么要确认参与计算的字段集合和业务定义的相等语义一致——比如继承关系下父类字段有没有正确纳入集合类字段参与计算时要保证不可变、不会在放入哈希表之后被修改如果手动排除了部分字段要保证equals和hashCode排除的是同一批字段不能两边字段集合对不上否则照样违反契约。Q6equals 里用 instanceof 判断类型可能有什么问题如果这个类被继承子类通常会在equals里多比较自己新增的字段。这样一来父类对象和子类对象比较时父类的equals用instanceof可能判定两者相等但子类的equals会因为多出来的字段判定不相等导致a.equals(b)和b.equals(a)结果不一致破坏了equals契约里的对称性。改用getClass()做严格的类型比较能规避这个问题代价是父类和子类对象之间永远不可能相等。下一篇预告模块二到这里就收尾了Day21 开始进入模块三集合框架ArrayList和LinkedList到底该怎么选从源码层面把两者的性能差异掰开揉碎讲清楚。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

基于STM32的智能鱼缸系统设计:从传感器到自动控制的嵌入式实战 2026/8/31 14:44:59

基于STM32的智能鱼缸系统设计:从传感器到自动控制的嵌入式实战

简介:本资源是一套完整的STM32嵌入式毕业设计项目,面向电子信息、自动化及物联网方向的本科生与嵌入式初学者,解决智能水产养殖系统中环境监测、自动调控与远程交互等典型工程问题。压缩包共98个文件,含41个.h头文件(定…

阅读更多 →
人形机器人下一程:从“羞答答”夺冠到系统稳定与工程落地 2026/8/31 14:44:59

人形机器人下一程:从“羞答答”夺冠到系统稳定与工程落地

人形机器人在最近的这场焦点赛事里夺冠,围观者关注的是它“羞答答”的步态和小心翼翼的动作,行业内更在意的却是另外一件事:下一程到底往哪走。这类新闻隔一段时间就会出现,但今年明显不一样,夺冠不再靠某个惊艳动作&a…

阅读更多 →
开源工具将Pull Request变成动画架构图,让结构变化一目了然 2026/8/31 14:44:59

开源工具将Pull Request变成动画架构图,让结构变化一目了然

接手一个新仓库,或者评审一个改动范围比较大的 PR 时,最消耗精力的往往不是读代码本身,而是先要在脑子里拼出“这次改动到底动了架构的哪一块”。文件多了之后,人的短期记忆根本装不下完整的调用链和依赖关系,评审就很…

阅读更多 →
AI编程利器:用Skill自动生成流程图,告别手搓 2026/8/31 14:44:59

AI编程利器:用Skill自动生成流程图,告别手搓

做技术这么多年,我越来越觉得,画流程图这件事,快成了开发者的“时间黑洞”。为什么这么说?你可以回忆一下:接到一个需求,代码逻辑其实想清楚了,但leader让你“先画个流程图确认一下”&#xff1…

阅读更多 →
CT脊柱精细分割与三维智能测量全流程实践 2026/8/31 14:44:59

CT脊柱精细分割与三维智能测量全流程实践

简介:本资源是一套面向医学影像AI研发者、骨科临床工程师及三维建模研究者的高精度CT脊柱结构分割数据集,旨在解决脊柱自动分割精度低、解剖结构覆盖不全、病理泛化能力弱等关键问题,支撑三维重建、椎体测量、手术导航等下游应用。压缩包共93…

阅读更多 →
PrivaZer深度清理指南:清除隐私残留与释放C盘空间 2026/8/31 14:39:58

PrivaZer深度清理指南:清除隐私残留与释放C盘空间

电脑里的隐私残留数据,比大多数人想象的多。删除文件、清空回收站以后,数据并不是立即消失,它可能残留在临时目录、浏览器缓存、缩略图库、日志文件、系统还原点甚至未分配空间中。对于经常外借电脑、准备出售旧硬盘,或者发现C盘爆…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞