JAVA中为什么要一起重写equals和hashCode方法?
在Effective Java一书中,提到了一点:在每个覆盖了equals方法的类中,都必须覆盖hashCode方法。为什么要有这个要求呢?
这一要求与HashMap,HashTable等基于hash的集合的工作机制有关,如果不同时提供equals和hashCode方法,可能会导致这类集合无法正常工作。
HashMap的工作机制
HashMap是我们常用的一个集合类,它可以映射键值对,非常方便。
在后台,它把我们的键值对打包成一个Entry<key,value>,然后将这个Entry存起来。当使用键查找时,HashMap会在存放Entry的地方查找符合要求的键,然后返回存储的值。
存放的地方是一个桶,以4个大小的桶为例,其存放方式如下所示(暂不考虑java8的红黑树方式):

当一个元素过来时,HashMap会先根据key的hashCode计算出需要存放在哪个位置,比如键a本来的hashCode的值为47,通过 mod 4(取余数)计算得到可以存放在位置3上,然后发现位置3不为空,找到位置3的链表,然后对里面的元素依次进行equals比较,发现相同的则认为是更新,否则则认为是新增一个节点,会将新的节点放在这个链表的头部。
hashCode不正确对HashMap的影响
从上面的简单介绍可以发现,HashMap会使用hashCode作为其判断桶里面位置的依据,如果hashCode不正确,可能会有以下问题:
- 只实现了
equals没有定义hashCode,可能会导致put和get不正常,不是以想要的方式运行。 - 实现的
hashCode不均匀散列,导致数据都堆到一个位置,影响HashMap的性能。
下面详细介绍这两种情况。
只实现了equals没有定义hashCode
只实现了equals,hashCode就会是Object默认的实现,一般是每个不同的对象的hashCode都不一样。下面是一个简单的例子:
class Person{
private String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person person)) return false;
return Objects.equals(name, person.name);
}
}
这个类只有一个字段,而且只实现了equals,下面是把这个类当成key,来存放数据,我们测试一下:
public static void main(String[] args) {
Map<Person, String> testMap = new HashMap<>();
testMap.put(new Person("Zhang San"), "test val");
System.out.println(testMap.get(new Person("Zhang San")));
}
我们用new Person("Zhang San")作为key,存了一个值test val,然后尝试取出这个值,结果却大出所料:有时输出为null,有时输出test val,也就是说,会在某些情况下无法取出这个值。发生了什么呢?
还是用刚才的图来说明一下发生了什么:

我们存入时,假设键new Person("Zhang San")的hashCode为1,存入位置1,取数据时,new了一个新的对象,hashCode为2(默认每个对象的hashCode都不一样),它落到了位置2,这时发现位置2没有数据,就返回null了。当取数据new的对象的散列值为类似5这类时,计算的存放位置为1,发现1里面有数据,把链表里面的数据取出来做比较,第一个equals就相等了,就可以返回test val。
所以,在实现equals时,一定要实现hashCode。
实现的hashCode不均匀散列
先说正确的实现方式:可以直接使用Objects.hash方法,实现的结果就比较好了。比如上面的例子可以实现为:return Objects.hash(name);。
在IDEA中,还可以直接在类里面使用快捷键Alt+Insert,在弹出窗口选择equals() 和 hashCode(),直接一键生成。
继续介绍不均匀的散列,比如我们实现了一个散列函数: return 10。还是上面的例子,我们会发现所有的数据都会映射到位置2,结果查找数据就成了链表查找,时间复杂度从O(1)降低到了O(n),效率会非常差。
总结
综上所述,实现equals就必须要实现hashCode方法,否则会导致基于hash的一些集合无法正确工作。