iT邦幫忙

2024 iThome 鐵人賽

DAY 12
0
Software Development

深入淺出Java 30天系列 第 12

Day 12: 覆寫equals時,也要覆寫hashCode(下)

  • 分享至 

  • xImage
  •  

昨天提到了覆寫equals和覆寫hashCode需要注意的事情,今天就來談談,覆寫hashCode可以遵照哪些規則覆寫。

  • 沒有在equals用來判斷物件相不相等的欄位,最好在hashCode裡面也不要拿來計算hash code。

  • 一開始可以先指定一個是型別是int的非0值(例如17),作為初始值存在result裡面。

    書上說沒加初始值會增加hash碰撞,但我在想範例的時候測了幾次發現,有加沒加初始值似乎都會遇到一樣的碰撞問題,反而是把hash值差異比較大的欄位先計算,比較能夠避免碰撞。

    以下面的範例為例,一開始先計算型別為booleanisChild ,結果在name一樣的情況下,age一個10一個41會發生碰撞。

    class PersonForHash {
      private boolean isChild;
      private String name;
      private int age;
    
      public PersonForHash(boolean isChild, String name, int age) { 
          this.isChild = isChild;
          this.name = name;
          this.age = age;
      }
    
      @Override
      public int hashCode() {
          int result = 17;
          result = 31 * result + (isChild ? 1 : 0); 
          result = 31 * result + age;
          result = 31 * result + (name != null ? name.hashCode() : 0);
          return result;
      }
    
      public static void main(String[] args) {
          PersonForHash p1 = new PersonForHash(true, "Lucky", 10);
          PersonForHash p2 = new PersonForHash(false, "Lucky", 41); 
          System.out.println("Hash code of p1: " + p1.hashCode()); //74279438
          System.out.println("Hash code of p2: " + p2.hashCode()); //74279438
      }
    

    但如果上面的例子先計算age,再計算isChildp1的hash code會是74287808,p2是74317568,會有些許的差距。

  • 對於類別中的欄位,可以根據不同型別,用以下方式計算hash code。

    • 欄位型別是booleantrue的hash code是1,false是0 => (f ? 1 : 0)
    • 型別是bytecharshortint,強制轉換成int後,即是hash code => (int) f
    • 型別是long,先右移32 bit之後再XOR自己,最後轉成int => (int)(f^(f>>>32))
    • 型別是float,可以用Float.floatToIntBits幫忙轉換=> Float.floatToIntBits(f)
    • 型別是double,先轉成long,再做一次型別是long的轉換步驟 => long bits = Double.doubleToLongBits(f) => (int) (bits ^ (bits >>> 32))
    • 型別是各式各樣的物件的話,可以直接呼叫該物件的hashCode,但如果該欄位的結果是null,直接用0當hash code
    • 如果是array,可以把array中的每個element當成獨立欄位,每個element都獨自根據上面的原則計算hash code。java 1.5版之後,也可以用Arrays.hashCode計算整個array的hash code。
  • 最後,用下面的公式去計算整個類別的hash code,c代表每個欄位的hash code。而為什麼是用31去乘以result呢?因為31是質數,無法由其他數字組成,所以hash code與31相乘之後,發生碰撞的機率較低,另外,31 * result電腦的處理器可以翻譯成(i << 5) - i去運算,對於處理器來說,與31相乘的performance會比其他數字更好。

result = 31 * result + c
  • 計算完hash code之後,別忘了撰寫unit test檢查,兩個equalstrue的物件,hash code有沒有相同。

關於覆寫equals和覆寫hashCode的部分,這幾天大概都說得差不多了~


上一篇
Day 11: 覆寫equals時,也要覆寫hashCode(上)
下一篇
Day 13: 明智地覆寫clone(上)
系列文
深入淺出Java 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言