1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【09期】說說hashCode() 和 equals() 之間的關(guān)系?

        共 7415字,需瀏覽 15分鐘

         ·

        2020-08-09 08:49

        程序員的成長之路
        互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
        關(guān)注


        閱讀本文大概需要 6 分鐘。

        來自:java面試題精選

        上一篇關(guān)于介紹Object類下的幾種方法時面試題時,提到equals()和hashCode()方法可能引出關(guān)于“hashCode() 和 equals() 之間的關(guān)系?的面試題,本篇來解析一下這道基礎(chǔ)面試題。
        先祭一張圖,可以思考一下為什么?

        介紹

        equals()?的作用是用來判斷兩個對象是否相等。
        hashCode()?的作用是獲取哈希碼,也稱為散列碼;它實(shí)際上是返回一個int整數(shù)。這個哈希碼的作用是確定該對象在哈希表中的索引位置。

        關(guān)系

        我們以“類的用途”來將“hashCode() 和 equals()的關(guān)系”分2種情況來說明。

        1、不會創(chuàng)建“類對應(yīng)的散列表”

        這里所說的“不會創(chuàng)建類對應(yīng)的散列表”是說:我們不會在HashSet, Hashtable, HashMap等等這些本質(zhì)是散列表的數(shù)據(jù)結(jié)構(gòu)中,用到該類。例如,不會創(chuàng)建該類的HashSet集合。
        在這種情況下,該類的“hashCode() 和 equals() ”沒有半毛錢關(guān)系的!equals() 用來比較該類的兩個對象是否相等。而hashCode() 則根本沒有任何作用。
        下面,我們通過示例查看類的兩個對象相等 以及 不等時hashCode()的取值。
        import?java.util.*;
        import?java.lang.Comparable;

        /**
        ?*?@desc 比較equals()?返回true 以及?返回false時, hashCode()的值。
        ?*
        ?*/

        public?class?NormalHashCodeTest{

        ????public?static?void?main(String[]?args)?{
        ????????//?新建2個相同內(nèi)容的Person對象,
        ????????//?再用equals比較它們是否相等
        ????????Person?p1?=?new?Person("eee",?100);
        ????????Person?p2?=?new?Person("eee",?100);
        ????????Person?p3?=?new?Person("aaa",?200);
        ????????System.out.printf("p1.equals(p2)?:?%s;?p1(%d)?p2(%d)\n",?p1.equals(p2),?p1.hashCode(),?p2.hashCode());
        ????????System.out.printf("p1.equals(p3)?:?%s;?p1(%d)?p3(%d)\n",?p1.equals(p3),?p1.hashCode(),?p3.hashCode());
        ????}

        ????/**
        ?????*?@desc Person類。
        ?????*/

        ????private?static?class?Person?{
        ????????int?age;
        ????????String?name;

        ????????public?Person(String?name,?int?age)?{
        ????????????this.name?=?name;
        ????????????this.age?=?age;
        ????????}

        ????????public?String?toString()?{
        ????????????return?name?+?"?-?"?+age;
        ????????}

        ????????/**?
        ?????????*?@desc?覆蓋equals方法?
        ?????????*/
        ??
        ????????public?boolean?equals(Object?obj){??
        ????????????if(obj?==?null){??
        ????????????????return?false;??
        ????????????}??

        ????????????//如果是同一個對象返回true,反之返回false??
        ????????????if(this?==?obj){??
        ????????????????return?true;??
        ????????????}??

        ????????????//判斷是否類型相同??
        ????????????if(this.getClass()?!=?obj.getClass()){??
        ????????????????return?false;??
        ????????????}??

        ????????????Person?person?=?(Person)obj;??
        ????????????return?name.equals(person.name)?&&?age==person.age;??
        ????????}?
        ????}
        }
        運(yùn)行結(jié)果:
        p1.equals(p2)?:?true;?p1(1169863946)?p2(1901116749)
        p1.equals(p3)?:?false;?p1(1169863946)?p3(2131949076)
        從結(jié)果也可以看出:p1和p2相等的情況下,hashCode()也不一定相等。

        2、會創(chuàng)建“類對應(yīng)的散列表”

        這里所說的“會創(chuàng)建類對應(yīng)的散列表”是說:我們會在HashSet, Hashtable, HashMap等等這些本質(zhì)是散列表的數(shù)據(jù)結(jié)構(gòu)中,用到該類。例如,會創(chuàng)建該類的HashSet集合。
        在這種情況下,該類的“hashCode() 和 equals() ”是有關(guān)系的:
        • 如果兩個對象相等,那么它們的hashCode()值一定相同。這里的相等是指,通過equals()比較兩個對象時返回true。

        • 如果兩個對象hashCode()相等,它們并不一定相等。因?yàn)樵谏⒘斜碇?,hashCode()相等,即兩個鍵值對的哈希值相等。然而哈希值相等,并不一定能得出鍵值對相等。補(bǔ)充說一句:“兩個不同的鍵值對,哈希值相等”,這就是哈希沖突。

        此外,在這種情況下。若要判斷兩個對象是否相等,除了要覆蓋equals()之外,也要覆蓋hashCode()函數(shù)。否則,equals()無效。
        舉例,創(chuàng)建Person類的HashSet集合,必須同時覆蓋Person類的equals() 和 hashCode()方法。?
        如果單單只是覆蓋equals()方法。我們會發(fā)現(xiàn),equals()方法沒有達(dá)到我們想要的效果。
        import?java.util.*;
        import?java.lang.Comparable;

        /**
        ?*?@desc 比較equals()?返回true 以及?返回false時, hashCode()的值。
        ?*
        ?*/

        public?class?ConflictHashCodeTest1{

        ????public?static?void?main(String[]?args)?{
        ????????//?新建Person對象,
        ????????Person?p1?=?new?Person("eee",?100);
        ????????Person?p2?=?new?Person("eee",?100);
        ????????Person?p3?=?new?Person("aaa",?200);

        ????????//?新建HashSet對象?
        ????????HashSet?set?=?new?HashSet();
        ????????set.add(p1);
        ????????set.add(p2);
        ????????set.add(p3);

        ????????//?比較p1?和?p2,?并打印它們的hashCode()
        ????????System.out.printf("p1.equals(p2)?:?%s;?p1(%d)?p2(%d)\n",?p1.equals(p2),?p1.hashCode(),?p2.hashCode());
        ????????//?打印set
        ????????System.out.printf("set:%s\n",?set);
        ????}

        ????/**
        ?????*?@desc Person類。
        ?????*/

        ????private?static?class?Person?{
        ????????int?age;
        ????????String?name;

        ????????public?Person(String?name,?int?age)?{
        ????????????this.name?=?name;
        ????????????this.age?=?age;
        ????????}

        ????????public?String?toString()?{
        ????????????return?"("+name?+?",?"?+age+")";
        ????????}

        ????????/**?
        ?????????*?@desc?覆蓋equals方法?
        ?????????*/
        ??
        ????????@Override
        ????????public?boolean?equals(Object?obj)
        {??
        ????????????if(obj?==?null){??
        ????????????????return?false;??
        ????????????}??

        ????????????//如果是同一個對象返回true,反之返回false??
        ????????????if(this?==?obj){??
        ????????????????return?true;??
        ????????????}??

        ????????????//判斷是否類型相同??
        ????????????if(this.getClass()?!=?obj.getClass()){??
        ????????????????return?false;??
        ????????????}??

        ????????????Person?person?=?(Person)obj;??
        ????????????return?name.equals(person.name)?&&?age==person.age;??
        ????????}?
        ????}
        }
        運(yùn)行結(jié)果:
        p1.equals(p2)?:?true;?p1(1169863946)?p2(1690552137)
        set:[(eee,?100),?(eee,?100),?(aaa,?200)]
        結(jié)果分析:
        我們重寫了Person的equals()。但是,很奇怪的發(fā)現(xiàn):HashSet中仍然有重復(fù)元素:p1 和 p2。為什么會出現(xiàn)這種情況呢?
        這是因?yàn)殡m然p1 和 p2的內(nèi)容相等,但是它們的hashCode()不等;所以,HashSet在添加p1和p2的時候,認(rèn)為它們不相等。
        那同時覆蓋equals() 和 hashCode()方法呢?
        import?java.util.*;
        import?java.lang.Comparable;

        /**
        ?*?@desc 比較equals()?返回true 以及?返回false時, hashCode()的值。
        ?*
        ?*/

        public?class?ConflictHashCodeTest2{

        ????public?static?void?main(String[]?args)?{
        ????????//?新建Person對象,
        ????????Person?p1?=?new?Person("eee",?100);
        ????????Person?p2?=?new?Person("eee",?100);
        ????????Person?p3?=?new?Person("aaa",?200);
        ????????Person?p4?=?new?Person("EEE",?100);

        ????????//?新建HashSet對象?
        ????????HashSet?set?=?new?HashSet();
        ????????set.add(p1);
        ????????set.add(p2);
        ????????set.add(p3);

        ????????//?比較p1?和?p2,?并打印它們的hashCode()
        ????????System.out.printf("p1.equals(p2)?:?%s;?p1(%d)?p2(%d)\n",?p1.equals(p2),?p1.hashCode(),?p2.hashCode());
        ????????//?比較p1?和?p4,?并打印它們的hashCode()
        ????????System.out.printf("p1.equals(p4)?:?%s;?p1(%d)?p4(%d)\n",?p1.equals(p4),?p1.hashCode(),?p4.hashCode());
        ????????//?打印set
        ????????System.out.printf("set:%s\n",?set);
        ????}

        ????/**
        ?????*?@desc Person類。
        ?????*/

        ????private?static?class?Person?{
        ????????int?age;
        ????????String?name;

        ????????public?Person(String?name,?int?age)?{
        ????????????this.name?=?name;
        ????????????this.age?=?age;
        ????????}

        ????????public?String?toString()?{
        ????????????return?name?+?"?-?"?+age;
        ????????}

        ????????/**?
        ?????????*?@desc重寫hashCode?
        ?????????*/
        ??
        ????????@Override
        ????????public?int?hashCode()
        {??
        ????????????int?nameHash?=??name.toUpperCase().hashCode();
        ????????????return?nameHash?^?age;
        ????????}

        ????????/**?
        ?????????*?@desc?覆蓋equals方法?
        ?????????*/
        ??
        ????????@Override
        ????????public?boolean?equals(Object?obj)
        {??
        ????????????if(obj?==?null){??
        ????????????????return?false;??
        ????????????}??

        ????????????//如果是同一個對象返回true,反之返回false??
        ????????????if(this?==?obj){??
        ????????????????return?true;??
        ????????????}??

        ????????????//判斷是否類型相同??
        ????????????if(this.getClass()?!=?obj.getClass()){??
        ????????????????return?false;??
        ????????????}??

        ????????????Person?person?=?(Person)obj;??
        ????????????return?name.equals(person.name)?&&?age==person.age;??
        ????????}?
        ????}
        }
        運(yùn)行結(jié)果:
        p1.equals(p2)?:?true;?p1(68545)?p2(68545)
        p1.equals(p4)?:?false;?p1(68545)?p4(68545)
        set:[aaa?-?200,?eee?-?100]
        結(jié)果分析:
        這下,equals()生效了,HashSet中沒有重復(fù)元素。
        比較p1和p2,我們發(fā)現(xiàn):它們的hashCode()相等,通過equals()比較它們也返回true。所以,p1和p2被視為相等。
        比較p1和p4,我們發(fā)現(xiàn):雖然它們的hashCode()相等;但是,通過equals()比較它們返回false。所以,p1和p4被視為不相等。

        原則

        1.同一個對象(沒有發(fā)生過修改)無論何時調(diào)用hashCode()得到的返回值必須一樣。
        如果一個key對象在put的時候調(diào)用hashCode()決定了存放的位置,而在get的時候調(diào)用hashCode()得到了不一樣的返回值,這個值映射到了一個和原來不一樣的地方,那么肯定就找不到原來那個鍵值對了。
        2.hashCode()的返回值相等的對象不一定相等,通過hashCode()和equals()必須能唯一確定一個對象。不相等的對象的hashCode()的結(jié)果可以相等。hashCode()在注意關(guān)注碰撞問題的時候,也要關(guān)注生成速度問題,完美hash不現(xiàn)實(shí)。
        3.一旦重寫了equals()函數(shù)(重寫equals的時候還要注意要滿足自反性、對稱性、傳遞性、一致性),就必須重寫hashCode()函數(shù)。而且hashCode()的生成哈希值的依據(jù)應(yīng)該是equals()中用來比較是否相等的字段。
        如果兩個由equals()規(guī)定相等的對象生成的hashCode不等,對于hashMap來說,他們很可能分別映射到不同位置,沒有調(diào)用equals()比較是否相等的機(jī)會,兩個實(shí)際上相等的對象可能被插入不同位置,出現(xiàn)錯誤。其他一些基于哈希方法的集合類可能也會有這個問題。

        推薦閱讀:

        畢設(shè)有著落了!一套開源的,基于SpringBoot的車牌識別系統(tǒng)

        飛天茅臺超賣事故:Redis分布式鎖請慎用!

        5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機(jī),樹莓派,等等。在公眾號內(nèi)回復(fù)「2048」,即可免費(fèi)獲?。?!

        微信掃描二維碼,關(guān)注我的公眾號

        寫留言

        朕已閱?

        瀏覽 43
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            精品人妻少妇嫩草AV蜜桃 | 久热中文字幕 | 水多多成人A片在线观看播放 | 爱涩av | 久久国产精久久精产国 | 亚洲精品无码少妇撅臀 | 91神马午夜 | 日韩一区二区中文字幕 | 国产乱淫AV免费 | 北条麻妃激情视频 |