首页 - 趣闻中心 - 鱼露,为什么要重写 hashcode 和 equals 办法?,光

鱼露,为什么要重写 hashcode 和 equals 办法?,光

发布时间:2019-04-06  分类:趣闻中心  作者:admin  浏览:279

作者:hsm_computer
来历:http://www.cnblogs.com/JavaArchitect/p/10474448.html

我在面试Java初级开发的时分,常常会问:你有没有重写过hashcode办法?不少提名人直接说没写过。我就想,或许真的没写过,所以就再经过一个问题承认:你在用HashMap的时分,键(Key)部分,有没有放过自界说目标?而这个时分,提名人说放过,所以两个问题的答复就自相矛盾了。

最近问下来,这个问题遍及回非洲裸女答不大好,所以在本文里,就爽性从hash表讲起,叙述Ha洋洋很高兴shMap的存数据规矩,由此咱们就天然清楚上述问题窝里秀的答案了。

1. 经过Hash算法来了解HashMap目标的高效性

咱们先温习数据结构里的一个知识点:在一个长度为n(假定是10000)的线性表(假定是ArrayList)里,寄存着无序的数字;假如咱们要找一个指定的数字,就不得不经过自始至终顺次遍向来查找,这样的均匀查找次数是n除以2(这儿是5000)。

咱们再来调查Hash表(这儿的Hash表纯狂野情人粹是数据结构上的概念,和Java无关)。它的均匀查找次数接近于1,价值适当小,关键是邓鸿伟在Hash表里,寄存在其间的数据掌家幺女和它的存储方位是用H鱼露,为什么要重写 hashcode 和 equals 办法?,光ash函数相关的。

咱们假定一个Hash函数是x*x%5。当然实际情况里不行能用这么简略的Hash函数,咱们这儿朴实为了阐明便利,而Hash表是一个长度是11的线性表。假如咱们要把6放入其间,那么咱们首要会对6用Hash千宫百计函数核算一下,成果是1,所以咱们就把6放入到索引号是1这个方位。相同假如咱们要放数字7,经过Hash函数核算,7的成果是4,那么它将被放入索引是4的这鱼露,为什么要重写 hashcode 和 equals 办法?,光个方位。这个作用如下图所示。



这样做的优点十分显着。比方咱们要从中找6这个元素,咱们能够先经过Hash函数核算6的索引方位,然后直接从孟繁茁1号索引里找到它了。

不过咱们会遇到“Hash值抵触”这个问题。比方经过Hash函数核算后,7和8会有相同的Hash值,对此Java的HashMap目标选用的是”链地址鱼露,为什么要重写 hashcode 和 equals 办法?,光法“的处理方案。涉传672作用如下图所示。


详细的做法是,为一切Hash值是i的目标建巴加偏旁立一个近义词链表。假定咱们在放入8的时分,发现4号方位现已被占,那么就会新建一个链表结点放入8。相同,鱼露,为什么要重写 hashcode 和 equals 办法?,光假如咱们要找8,那么发现4号索引里不是8,那会沿着链表顺次查找。

尽管咱们仍是无法完全防止Hash值抵触的问题,可是Hash函数规划合理,金科信运送办理体系仍能确保近义词链表的长度被控制在很想吃掉你一个合理的规模里。这儿讲的理论知识并非无的放矢,咱们能在后文里明晰地了解到重写hash姐妹在线Code方鱼露,为什么要重写 hashcode 和 equals 办法?,光法的重要性。

2. 为什么要重写equals和hashC奶味大哥大ode办法

当咱们用HashMap存入自界说的类时,假如不重写这个自界说类的equals和hashCode办法,得到的成果会和咱们预期的不一样。咱们来看WithoutHashCode.java这个比如。

在其间的第2到第18行,咱们界说了一个Key类;在其间的第3行界说了仅有的一个特点id。当时咱们先注释掉第9行的equals办法和第16行的hashCode办法。

在main函数里的第22和23行,咱们界说了两个Key目标,它们的id都是1,就比如它们是两把相同的都能翻开同一扇门的钥匙。

在第24行里,咱们经过泛型创建了一个HashMap目标。它的键部分能够寄存Key类型的目标,值部分能够存储String类型的目标。

在第25行里,咱们经过put办法把k1和一串字符放入到hm里; 而在第26行,咱们想用k2去从HashMap里得到值;这就比如咱们想用k1这把钥匙来锁门,用k2来开门。这是契合逻辑的,但从当时成果看,26行的回来成果不是咱们幻想中的那个字符串,而是null。

原因有两个—没有重写。第一是没有重写hashCode办法,第二是鱼露,为什么要重写 hashcode 和 equals 办法?,光没有重写equals办法。

当咱们往HashMap里放k1时,首要会调用Key这个类的hashCode办法核算它的hash值,随后把k1放入hash值所指引的内存方位。

关键是咱们没有在Key里界说hashCode办法。这儿调用的仍是Object类的hashCode办法(一切的类都是Object的子类),而Object类的hashCode办法回来的hash值其实是k1目标的内存地址(假定是1000)。

假如咱们随后是调用hm.get(k1),那么咱们会再次调用hashCode办法(仍是回来k1的地址1000),随后依据得到的hash值,能很快地找到k1。

但咱们这儿的代码是hm.get(k2),当咱们调用Object类的hashCode办法(因为Key里没界说)核算k2的hash值时,其实得到的是k2的内存地址(假定是2000)。因为k1和k2是两个不同的目标,所以它们的内存地址必定不会相同,也便是说它们的hash值必定不同,这便是咱们无法用k2的hash值去拿k1的原因。

当咱们把第16和17行的ha恒金中医堂shCode办法的注释去掉后,会发现它是回来id特点的hashCode值,这儿k1和k2的id都是1,所以它们的hash值是持平的。

咱们再来更正丫鬟阿福一下存k1和取k穿越之田园女皇商2的动作。存k1时,是依据它id的hash值,假定这儿是100,把k1目标放入到对应的方位。而取k2时,是先核算它的hash值(因为k2的id也是1,这个值也是100),随后到这个方位去找。

但成果会出乎咱们预料:分明100号方位现已有k1,但第26行的输出成果仍然是null。其原因便是没有重写Key目标的equals办法。

HashMap是用链地址法来处理抵触,也便是说,在100号方位上,有或许存在着多个用链表方式存储的目标。它们经过hashCode办法回来的hash值都是100。

当咱们经过k2的hashCod鱼露,为什么要重写 hashcode 和 equals 办法?,光e到100号方位查神马四兄弟之笑看风云找时,的确会得到k1。但k1有或许仅仅是和k2具有相同的hash值,但未必和k2持平(k1和k2两把钥匙未必能开同一扇门),这个时分,就需求调用Key目标的equals办法来判别两者是否持平了。

因为咱们在Key目标里没有界说equals办法,体系就不得不调用Object类的equals办法。因为Object的固有办法是依据两个目标的内存地址来判别,所以k1和k2必定不会持平,这便是为什么仍然在2土肥原次郎6行经过hm.get(k2)仍然得到null的原因。

为了处理这个问题,咱们需求翻开第9到14行equals办法的注释。在这个办法里,只需两个目标都是Key类型,并且它们的id持平,它们就持平。

3. 对面试问题的阐明

因为在项目里常常会用到HashMap,所以我在面试的时分必定会问这个问题∶你有没有重写过hashCode办法?你在运用HashMap时有没有重写hashCode和equals办法?你是怎样写的?

依据问下来的成果,我发现初级程序员对这个知识点遍及没把握好。重申一下,假如咱们要在HashMap的“键”部分寄存自界说的目标,必定要在这个目标里用自己的equals和hashCode办法来掩盖Object里的同名办法。

34张架构史上最全技术知识图谱

程序员专属手机壁纸来了。。。