点击上方
阅读本文大概需要 4.6 分钟
前言
在日常开发中,我们有时会重写hashCode()和equals()方法,但为什么需要重写呢?跟着我的思路来看。
Hash算法
在
你真的了解HashMap吗?(上) 这篇文章中,介绍了一下Hash的基础知识,我们再举个小栗子来了解下Hash。
![d5dd6445b3d6f399cacccac34896f94f.png](https://img-blog.csdnimg.cn/img_convert/d5dd6445b3d6f399cacccac34896f94f.png)
神兽学编程 关注我吧
预计时间![5e0c2d0c35b24c867e047026420bf565.png](https://img-blog.csdnimg.cn/img_convert/5e0c2d0c35b24c867e047026420bf565.png)
假设Hash表长度为8,Hash函数为 x*x%5(当然实际函数不会这么简单)。
同理,如果要放元素7,hash结果为4,那么元素7就会放到下标为4的位置。
比如我们在存放元素8时,发现4号位置已经被占了,则会封装一个新节点存放元素8,链接在元素7后面,效果如下所示。
我们使用以下代码来进行验证:
1public class MyKey {
2
3 private Integer keyId;
4
5 public MyKey(Integer keyId) {
6 this.keyId = keyId;
7 }
8
9 // @Override
10 // public int hashCode() {
11 // return keyId.hashCode();
12 // }
13
14 // @Override
15 // public boolean equals(Object o) {
16 // if (this == o) return true;
17 // if (o == null || getClass() != o.getClass()) return false;
18 // MyKey key = (MyKey) o;
19 // return keyId.equals(key.keyId);
20 // }
21
22 public static void main(String[] args) {
23 MyKey key1 = new MyKey(1);
24 MyKey key2 = new MyKey(1);
25 Map map = new HashMap<>(4);26 map.put(key1, "keyId为1");27 System.out.println(map.get(key2));28 }29}
定义了两个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()方法之后就能顺利取到值了。