为什么要重写hashCode方法?

Posted by 好记性不如烂笔头 on 04-27,2020

什么是hashCode方法?

hashCode方法是java的终极父类Object中的一个方法,本身是一个空方法。

hashcode方法

由于这个方法在Object类中,所以任何类都会有这个方法(因为任何类都是Object的子类)。

hashCode方法有什么用?

这个方法返回了一个哈希码,作为一个实例对象在虚拟机堆中的标识。

相同的对象的哈希码必定相同。

不同的对象的哈希码可以相同,但尽量要保持为不同。

为什么要重写hashCode方法?

理论上讲,hashCode方法就是为了Map类型的使用而存在的,如果某个类不作为Map的key的类型,我们可以完全不管这个hashCode方法,但是如果这个类作为了Map的key的类型,我们在编写这个类时就要做两件事了

  1. 重写equels方法
  2. 重写hashCode方法

为了解释第2点,先来复习一下第1点。当使用java中的==符号比较两个对象引用的时候,实际上比较的是这两个引用是否指向同一个堆区域(对象内容存储在JVM的堆中),指向不同堆区域的引用永远不会相等,即使他们两个对象的内容都完全相等。所以我们就要重写equels方法,让两个对象内容相同时就判定为相等,而不管是不是指向同一个堆区域!比如String类就是默认重写了equels方法的,其他我们自己写的类也必须重写这个方法。

回到重点,HashMap在使用put方法插入键值对的时候,会调用putVal方法,这个putVal方法是重点,它会依据key是否已经在Node中存在而采取不同的策略。如果key已经存在了,那么就更新这个key对应的value值;如果key不存在,那么就插入这个键值对到某个Node中。那么怎么判断key是否已经存在呢?这就用到了刚刚讲的equels方法和hashCode方法了。

putVal先用equels方法判断,再用hashCode方法判断。我们想要的效果是相同对象(内容相同就视为相同)用equels判断应该是相等的,它们的hashCode值也要是相等的!如果不重写hashCode方法,即使equels判断是相等的,hashCode值也不一定会相等!

而HashMap在调用get方法时也是通过key的hashCode值获取键值对放在数组中的位置,进而返回value的,这更要求了前面说的那个效果,不然的话,put进去一个键值对,再拿出来,很可能就不是原来的值。

下面的例子很好地说明了这个问题,建议仔细理解,如果你有一颗钻研的心的话:

编写一个Book类,重写equels方法,但不重写hashCode方法

Book举例hashcode方法

然后我们在demo中的主方法写下这么一些内容进行测试:

重写hashcode

创建了两个Book类的对象,分别为book1和book2,这两个对象的内容是相同的,而我重写了equels方法,所以用equels判断应该是相等的。但我们把<boo1, 10>和<book2, 20>这两个键值对先后添加到map中,按理来说,这两个对象是相等的,我们要的效果是:在第二次添加<book2, 20>时,应该更新前面添加的第一个键值对,也就是说结果map中应该只有一个键值对,而键值对的value应该是20。然而,我两次get的结果却是不同的:

两次不同的结果

这就说明,这个map中存放了两个键值对,不符合预期效果!所以必须重写hashCode方法!

在我把Book中的hashCode方法加上之后,结果符合预期,是更新后的20。

两次相同的结果

总结

放入Map中的类必须重写equels方法和hashCode方法,用来保证相同的key取回来的是相同的value。