阅读本文大概需要 4.6 分钟
欢迎大家关注我的公众号:神兽学编程
目录
1、前言
在日常开发中,我们有时会重写hashCode()和equals()方法,但为什么需要重写呢?跟着我的思路来看。
2、Hash算法
在 你真的了解HashMap吗?(上) 这篇文章中,介绍了一下Hash的基础知识,我们再举个小栗子来了解下Hash。
假设Hash表长度为8,Hash函数为 x*x%5(当然实际函数不会这么简单)。
如果我们要放入6这个元素,先计算6的hash值,结果为1,所以元素6就放到下标为1的位置。
同理,如果要放元素7,hash结果为4,那么元素7就会放到下标为4的位置。
使用Hash的好处非常明显,当我们需要查询某个元素时,只需要通过hash函数获取到元素下标位置,就能找到该元素。
不过可能会遇到Hash冲突问题,比如元素8经过hash计算后得到的结果也为4。对此,HashMap中采用 “链地址法” 解决该问题,封装一个Node采用尾插法连接到尾节点后。
比如我们在存放元素8时,发现4号位置已经被占了,则会封装一个新节点存放元素8,链接在元素7后面,效果如下所示。
同理,当我们需要查找元素8时,发现4号位置存放的不是8,则会沿着链表依次查找。
虽然Hash冲突无法避免,但只要Hash函数足够散列,还是能保证链表长度控制在合理的范围。
这里讲解的知识有助于下面了解重写hashCode()方法的重要性。
3、为什么需要重写这两个方法
当HashMap的key存放自定义对象时,如果不重写该类的hashCode()和equals()方法,得到的结果会和预期值不同。
我们使用以下代码来进行验证:
public class MyKey {
private Integer keyId;
public MyKey(Integer keyId) {
this.keyId = keyId;
}
// @Override
// public int hashCode() {
// return keyId.hashCode();
// }
// @Override
// public boolean equals(Object o) {
// if (this == o) return true;
// if (o == null || getClass() != o.getClass()) return false;
// MyKey key = (MyKey) o;
// return keyId.equals(key.keyId);
// }
public static void main(String[] args) {
MyKey key1 = new MyKey(1);
MyKey key2 = new MyKey(1);
Map<MyKey, String> map = new HashMap<>(4);
map.put(key1, "keyId为1");
System.out.println(map.get(key2));
}
}
定义了两个MyKey对象,key1和key2,keyId都为1,相当于是同一把锁的两把钥匙。代码中的操作相当于用key1去锁门,用key2去开门,按理key2应该也能打开这把锁,但实际情况并非如此。
原因有两个:
①、没有重写MyKey的hashCode()方法;
②、没有重写MyKey的equals()方法;
当我们往HashMap中存放key1时,会先调用key1对象的hashCode()方法计算hash值,随后放入寻址下标位置。
当我们没有重写该对象的hashCode()时,会默认调用Object类的hashCode()方法,而Object类的hashCode()返回的是对象的内存地址。
假设key1的内存地址为0x2501,key2的内存地址为0x3112(不同对象存储的内存位置不同);
那么调用 map.get(key2) ,使用的是key2的内存地址计算的hash值,得到的hash值自然和key1的hash值不相等,因此拿到的值为null。
当放开重写hashCode()方法的注释之后,hashCode()返回的是keyId的hash值,由于key1和key2的keyId相同,所以得到的hash值也相同,这里假设寻址下标位置为3。
但运行结果出乎意料,明明3号位置已经存储了key1,但输出结果还是为null。这是因为没有重写对象的equals()方法,系统调用了Object类的equals()方法,而Object类的equals()方法比较的是对象的内存地址。
key1和key2内存地址自然不相等,所以当重写了equals()方法后,变成比较两个对象中字段值是否相等。而key1的keyId字段值自然和key2的相等,所以当重写了hashCode()和equals()方法之后就能顺利取到值了。