为什么重写equals必须重写hashCode

为什么重写equals必须重写hashCode

1. Object中equals、hashCode

public native int hashCode();

public boolean equals(Object obj) {
        return (this == obj);
}

首先看一下equals方法,Object中默认比较两个对象的地址。也就是说如果一个类默认的equals,就比较地址。而反观hashCode方法,我们先来看一下hashCode注释

 /**
     * Returns a hash code value for the object.  

     //返回的是一个对象的hash值

     * This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>

     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, 

         //这里又是关键,无论调用多少次hashCode,同一个对象都会返回同一个hash值

     *     provided no information
     *     used in {@code equals} comparisons on the object is modified.

        // 这里没看懂 但是测试了一下,如果同一个对象属性变化了,它的hash值也会不变


     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.

         //如果equals比较两个对象相等,那么这两个对象必须就有相同的hash值

     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java&trade; programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */

再结合Effective Java 第三版中所提到的

每个覆盖了equals方法的类中,必须覆盖hashCode。如果不这么做,就违背了hashCode的通用约定,也就是上面注释中所说的。进而导致该类无法结合所以与散列的集合一起正常运作,这里指的是HashMap、HashSet、HashTable、ConcurrentHashMap。

2. equals和hashCode同时存在的意义

equalshashCode都是用来判断两个对象想不想等的,那么问题来了?

为什么需要两个呢?

  • equals - 保证比较对象是否是绝对相等的
  • hashCode - 保证在最快的时间内判断两个对象是否相等,可能有误差值

一个是保证可靠,一个是保证性能。也就是说:

  • 同一个对象的hashCode一定相等,不同对象的hashCode也可能相等,这是因为hashCode是根据地址hash出来的一个int 32 位的整型数字,相等是在所难免。
  • equals比较的是两个对象的地址,同一个对象地址肯定相同,不同的对象地址一定不同,可靠性是这么来的。

就像HashMap里面插入时判断:

if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))

判断两个key是否相同逻辑

  1. 先比较 hash (通过hashCode的高16位和低16位进行异或运算得出的) ,因为两个相同的对象hash值一定相等。
  2. 再比较两个对象的地址是否相同,== 判断是否绝对相等,而equals判断是否客观相等

下面分析一下简单的不重写hashCode的后果和在存到HashMap中可能出现的后果

3. 如果只重写equals的后果

  • 无论是Effective Java,还是阿里巴巴Java规范手册都是要求重写equals,必须重写hashCode。
  • 两个相等的对象必须具有相等的散列码(Java关键约定)

那么不重写的后果是什么呢?

举一个例子:

​ 如果一个只重写了equals(比较所有属性是否相等)的类 new 出了两个属性相同的对象。这时可以得到的信息是这个属性相同的对象地址肯定不同,但是equals是true,hashCode返回的是不相等的(一般不会出现hash碰撞)。

也就是说这个类对象违背了Java对于两个对象相等的约定。违背约定的原因是 可靠的equals判断两个对象是相等的,但是他们两个的散列码确是不相等的。

总结来说:

  • equals 为 true , hashCode 必须相等
  • hashCode 相等时 , equals 可以不用为 true (也就是hash碰撞的时候)

4 . 不重写HashCode对于散列表的影响

if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))

相同的场景,相同配方:

两个所有属性都相等的对象,但是地址不同。没重写hashCode时,p.hash == hash 一定不相等。但是逻辑上这两个对象是相等的,并且equals也是相等的。

​ 这就会导致,HashMap里面本来有这个key,但是你告诉我没有,导致了put操作成功。逻辑上是不符合规范的,get时取出来的也可能是自己另一个的value。

全部评论

相关推荐

2025-11-08 22:19
浙江科技大学 运营
Java转测开第一人:这种就是饼 把应届当廉价劳动力用完然后丢掉
你觉得今年秋招难吗
点赞 评论 收藏
分享
2025-12-08 16:04
门头沟学院 Java
本人本科末9,今年大三。大一大二一直玩,什么都没学到,在大学混日子混了两年,每天不是在打农就是在steam。大三开学时一个和自己玩的好的同学去实习了,才发现自己白白浪费了两年的时间,如果真不冲一下就真去京东,阿里,美团送外卖了今年9月份开始学Java,一开始一直跟着黑马视频看,后面发现看视频效率太低了,时间根本不够,就开始主要看文档和看书了。这几个月一直在学,真的尽力了,希望暑期前能找一份好点的实习。我简历上面的项目大多没有指标,但是实际上我是真没多少时间去做项目,我基本主要是动手只做了外卖和天机,黑马点评和12306我都是只是看了项目。主要是自己的时间真的不多,但是这样子自己的代码能力确实比较差。而且自己也没有做过实际的工程,我顶多用jmeter测试一下接口tps啥的,比如使用Redis管道提升了一点性能,减少Redis交互,这种值得写上去吗?需不需要具体到某些数字求求各位佬给一些建议,看看简历怎么优化?项目介绍是不是不够详细?没有具体到业务方面。项目会不会提到大致实现原理导致面试官一看简历就知道怎么实现就没有问的欲望?专业技能一些字段是不是要加粗,是不是写太啰嗦了?有没有必要压缩内容变成一页?两页的话是不是都要把两页填地满满的。
给秋招一个交代:一页简历最好,网上做的项目放面试官眼里都是玩具,简历上不需要强调有什么难点,记住就行防止真的问。然后背八股,多投多面试就行
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务