Java equals 和 hashCode 关系
早上收拾办公桌时,同事小杨突然凑过来问:“我刚写的用户登录校验,明明两个相同的手机号,用equals比却返回false,这是为啥?”其实他遇到的问题,正是Java里equals和 hashCode的关系没搞懂。很多写Java的新手,总把这俩方法当成“随便用的工具”,结果踩了各种坑,今天就用咱们日常买奶茶的例子,把这俩的关系说透。
先搞懂equals到底在比啥
equals可以理解成“一对一的精准比对”,就像你去奶茶店取餐,店员拿订单号核对,得是同一个订单才对;要是你说“我要这个柠檬茶”,店员拿另一个顾客的同订单号单子,自然对不上。

在Java里,默认的equals(Object类里的),比的是“对象的内存地址”——就像每个人的身份证号,就算你和双胞胎长得一样,身份证号也不一样。比如你new了两个new User("138xxxx1234")对象,他俩的手机号明明一样,但内存地址不同,默认equals就会返回false。
所以要让equals真的“按你想要的规则比”,得自己重写。比如User类里,重写equals只比手机号,那这两个对象的equals就会返回true。不过这里有个关键规则:重写equals后,还得对应重写hashCode,不然就会出大问题。
hashCode:相当于“取餐号的索引”
hashCode是Java给每个对象算的一个“哈希码”,可以理解成奶茶店的取餐号——它的作用是快速定位,不用挨个翻订单,直接按取餐号找就行。默认的hashCode,是根据内存地址算的,每个对象的哈希码基本都不一样。
Java里的集合类(比如HashMap、HashSet),用的是“先查hashCode,再用equals核对”的逻辑:先按对象的hashCode把它分到某个“桶”里,同一桶里的对象,再用equals比是不是真的相同。如果只重写equals,不重写hashCode,就会出现“同一个对象的hashCode不同”,导致集合里出现重复元素,或者取数据找不到的情况。
举个例子:你重写了User的equals,让两个同手机号的User返回true,但没改hashCode,那这两个对象的hashCode还是不同。放到HashSet里,它会觉得这俩是不同的对象,就会存两份;放到HashMap里,你存的是一个User当key,取的时候用另一个同手机号的User找,因为hashCode不同,找不到对应的值。
必须遵守的核心规则
这俩的关系,有个“双向绑定”的铁律:如果两个对象用equals比较返回true,那它们的hashCode必须相等;如果两个对象的hashCode相等,equals不一定返回true。
为啥说“hashCode相等,equals不一定相等”?因为哈希码是用算法算的,就算两个对象的内容完全不同,也可能算出同一个哈希码(这种情况叫“哈希碰撞”)——就像两个不同的订单,可能取餐号刚好一样,这时候需要再用equals核对,才不会搞错。
那怎么正确重写这俩方法?很简单:现在的IDE(比如IntelliJ)都有自动生成的功能,只要选要比对的字段(比如手机号),它就会帮你生成符合规则的equals和hashCode。别自己瞎写,不然很容易违反规则。
比如小杨后来用IDEA重写了User的equals和hashCode,再测试同手机号的两个对象,equals返回true,存进HashSet也只存了一份,登录校验终于能正常用了。
其实这俩方法的设计,本质是为了兼顾“精准比对”和“高效查找”——用hashCode先快速缩小范围,再用equals保证精准,这也是Java集合能高效工作的关键之一。搞懂这个关系,不仅能少写bug,还能理解Java面向对象设计里的“约定大于配置”的思路。