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>

        面試官:說(shuō)說(shuō)hashCode() 和 equals() 之間的關(guān)系?

        共 16300字,需瀏覽 33分鐘

         ·

        2021-09-01 19:09

        先祭一張圖,可以思考一下為什么?

        介紹

        equals() 的作用是用來(lái)判斷兩個(gè)對(duì)象是否相等。

        hashCode() 的作用是獲取哈希碼,也稱為散列碼;它實(shí)際上是返回一個(gè)int整數(shù)。這個(gè)哈希碼的作用是確定該對(duì)象在哈希表中的索引位置。

        關(guān)系

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

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

        這里所說(shuō)的“不會(huì)創(chuàng)建類對(duì)應(yīng)的散列表”是說(shuō):我們不會(huì)在HashSet, Hashtable, HashMap等等這些本質(zhì)是散列表的數(shù)據(jù)結(jié)構(gòu)中,用到該類。例如,不會(huì)創(chuàng)建該類的HashSet集合。

        在這種情況下,該類的“hashCode() 和 equals() ”沒(méi)有半毛錢(qián)關(guān)系的!equals() 用來(lái)比較該類的兩個(gè)對(duì)象是否相等。而hashCode() 則根本沒(méi)有任何作用。

        下面,我們通過(guò)示例查看類的兩個(gè)對(duì)象相等 以及 不等時(shí)hashCode()的取值。

        import java.util.*;
        import java.lang.Comparable;

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

        public class NormalHashCodeTest{

            public static void main(String[] args{
                // 新建2個(gè)相同內(nèi)容的Person對(duì)象,
                // 再用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;  
                    }  

                    //如果是同一個(gè)對(duì)象返回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、會(huì)創(chuàng)建“類對(duì)應(yīng)的散列表”

        這里所說(shuō)的“會(huì)創(chuàng)建類對(duì)應(yīng)的散列表”是說(shuō):我們會(huì)在HashSet, Hashtable, HashMap等等這些本質(zhì)是散列表的數(shù)據(jù)結(jié)構(gòu)中,用到該類。例如,會(huì)創(chuàng)建該類的HashSet集合。

        在這種情況下,該類的“hashCode() 和 equals() ”是有關(guān)系的:

        • 如果兩個(gè)對(duì)象相等,那么它們的hashCode()值一定相同。這里的相等是指,通過(guò)equals()比較兩個(gè)對(duì)象時(shí)返回true。

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

        此外,在這種情況下。若要判斷兩個(gè)對(duì)象是否相等,除了要覆蓋equals()之外,也要覆蓋hashCode()函數(shù)。否則,equals()無(wú)效。

        舉例,創(chuàng)建Person類的HashSet集合,必須同時(shí)覆蓋Person類的equals() 和 hashCode()方法。 

        如果單單只是覆蓋equals()方法。我們會(huì)發(fā)現(xiàn),equals()方法沒(méi)有達(dá)到我們想要的效果。

        import java.util.*;
        import java.lang.Comparable;

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

        public class ConflictHashCodeTest1{

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

                // 新建HashSet對(duì)象 
                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;  
                    }  

                    //如果是同一個(gè)對(duì)象返回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é)果分析:

        我們重寫(xiě)了Person的equals()。但是,很奇怪的發(fā)現(xiàn):HashSet中仍然有重復(fù)元素:p1 和 p2。為什么會(huì)出現(xiàn)這種情況呢?

        這是因?yàn)殡m然p1 和 p2的內(nèi)容相等,但是它們的hashCode()不等;所以,HashSet在添加p1和p2的時(shí)候,認(rèn)為它們不相等。

        那同時(shí)覆蓋equals() 和 hashCode()方法呢?

        import java.util.*;
        import java.lang.Comparable;

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

        public class ConflictHashCodeTest2{

            public static void main(String[] args{
                // 新建Person對(duì)象,
                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對(duì)象 
                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重寫(xiě)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;  
                    }  

                    //如果是同一個(gè)對(duì)象返回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中沒(méi)有重復(fù)元素。

        比較p1和p2,我們發(fā)現(xiàn):它們的hashCode()相等,通過(guò)equals()比較它們也返回true。所以,p1和p2被視為相等。

        比較p1和p4,我們發(fā)現(xiàn):雖然它們的hashCode()相等;但是,通過(guò)equals()比較它們返回false。所以,p1和p4被視為不相等。

        原則

        1.同一個(gè)對(duì)象(沒(méi)有發(fā)生過(guò)修改)無(wú)論何時(shí)調(diào)用hashCode()得到的返回值必須一樣。
        如果一個(gè)key對(duì)象在put的時(shí)候調(diào)用hashCode()決定了存放的位置,而在get的時(shí)候調(diào)用hashCode()得到了不一樣的返回值,這個(gè)值映射到了一個(gè)和原來(lái)不一樣的地方,那么肯定就找不到原來(lái)那個(gè)鍵值對(duì)了。

        2.hashCode()的返回值相等的對(duì)象不一定相等,通過(guò)hashCode()和equals()必須能唯一確定一個(gè)對(duì)象。不相等的對(duì)象的hashCode()的結(jié)果可以相等。hashCode()在注意關(guān)注碰撞問(wèn)題的時(shí)候,也要關(guān)注生成速度問(wèn)題,完美hash不現(xiàn)實(shí)。

        3.一旦重寫(xiě)了equals()函數(shù)(重寫(xiě)equals的時(shí)候還要注意要滿足自反性、對(duì)稱性、傳遞性、一致性),就必須重寫(xiě)hashCode()函數(shù)。而且hashCode()的生成哈希值的依據(jù)應(yīng)該是equals()中用來(lái)比較是否相等的字段。

        如果兩個(gè)由equals()規(guī)定相等的對(duì)象生成的hashCode不等,對(duì)于hashMap來(lái)說(shuō),他們很可能分別映射到不同位置,沒(méi)有調(diào)用equals()比較是否相等的機(jī)會(huì),兩個(gè)實(shí)際上相等的對(duì)象可能被插入不同位置,出現(xiàn)錯(cuò)誤。其他一些基于哈希方法的集合類可能也會(huì)有這個(gè)問(wèn)題

        程序汪資料鏈接

        程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理

        Java項(xiàng)目分享  最新整理全集,找項(xiàng)目不累啦 04版

        堪稱神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門(mén)到實(shí)戰(zhàn)進(jìn)階

        臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開(kāi)放下載!

        臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開(kāi)放下載!

        字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開(kāi)放下載!

        歡迎添加程序汪個(gè)人微信 itwang007  進(jìn)粉絲群或圍觀朋友圈

        瀏覽 42
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        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>
            一级片无遮挡 | 斗罗神女h玉腿抽搐泄 | 污污的网站入口 | 天干夜夜爽爽日日日日 | 亚洲成人娱乐 | www深夜成人 a√在线 | 主人调教女m跪趴sm视频 | 亚洲天堂男人 | 天天看夜夜爽 | 日批视频观看 |