<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 16300字,需瀏覽 33分鐘

           ·

          2021-09-01 19:09

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

          介紹

          equals() 的作用是用來判斷兩個對象是否相等。

          hashCode() 的作用是獲取哈希碼,也稱為散列碼;它實際上是返回一個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;  
                  } 
              }
          }

          運行結(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()相等,它們并不一定相等因為在散列表中,hashCode()相等,即兩個鍵值對的哈希值相等。然而哈希值相等,并不一定能得出鍵值對相等。補充說一句:“兩個不同的鍵值對,哈希值相等”,這就是哈希沖突。

          此外,在這種情況下。若要判斷兩個對象是否相等,除了要覆蓋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;  
                  } 
              }
          }

          運行結(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)這種情況呢?

          這是因為雖然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;  
                  } 
              }
          }

          運行結(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)實。

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

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

          程序汪資料鏈接

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

          Java項目分享  最新整理全集,找項目不累啦 04版

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

          臥槽!字節(jié)跳動《算法中文手冊》火了,完整版 PDF 開放下載!

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

          字節(jié)跳動總結(jié)的設(shè)計模式 PDF 火了,完整版開放下載!

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

          瀏覽 42
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  94自拍青 | 俺来也久草国产在线视频 | 伊人影院99 | 喷水人妻 | 无码毛片在线看 |