winform没有找到合适的方法来重写_为什么要重写hashCode()和equals()?

 点击上方 d5dd6445b3d6f399cacccac34896f94f.png

 神兽学编程 关注我吧

预计时间5e0c2d0c35b24c867e047026420bf565.png      阅读本文大概需要 4.6 分钟 前言 在日常开发中,我们有时会重写hashCode()和equals()方法,但为什么需要重写呢?跟着我的思路来看。 Hash算法 在  你真的了解HashMap吗?(上) 这篇文章中,介绍了一下Hash的基础知识,我们再举个小栗子来了解下Hash。

假设Hash表长度为8,Hash函数为 x*x%5(当然实际函数不会这么简单)。

ea9e24c6b53bc5c5df261c3b0942cded.png

如果我们要放入6这个元素,先计算6的hash值,结果为1,所以元素6就放到下标为1的位置。

同理,如果要放元素7,hash结果为4,那么元素7就会放到下标为4的位置。

27b8babd0a7d6ffa816b635479cc2682.png

使用Hash的好处非常明显,当我们需要查询某个元素时,只需要通过hash函数获取到元素下标位置,就能找到该元素。 不过可能会遇到Hash冲突问题,比如元素8经过hash计算后得到的结果也为4。对此,HashMap中采用 “链地址法” 解决该问题,封装一个Node采用尾插法连接到尾节点后。

比如我们在存放元素8时,发现4号位置已经被占了,则会封装一个新节点存放元素8,链接在元素7后面,效果如下所示。

a9068429cbedde6c3362f47bb6724ce0.png

同理,当我们需要查找元素8时,发现4号位置存放的不是8,则会沿着链表依次查找。 虽然Hash冲突无法避免,但只要Hash函数足够散列,还是能保证链表长度控制在合理的范围。 这里讲解的知识有助于下面了解重写hashCode()方法的重要性。 为什么需要重写这两个方法 当HashMap的key存放自定义对象时,如果不重写该类的hashCode()和equals()方法,得到的结果会和预期值不同。

我们使用以下代码来进行验证:

 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应该也能打开这把锁,但实际情况并非如此。

5d6cf16a5e20949ec0122c9c985e244e.png

原因有两个:

①、没有重写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()方法之后就能顺利取到值了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值