網頁

2020/5/25

Java Object.hashCode()

本篇筆記Object.hashCode()的理解。

Object.hashCode()用來支援HashMapHashSet等需要計算該物件的hash值(雜湊值)做為存放位置的索引(bucket number)或比較彼此不同的情況。

Object.hashCode()合約規範(general contract of hashCode)如下。

  1. 在一個Java應用程式執行中,若equals()中用來比較的資訊(通常是物件的屬性的hashCode()值)沒被修改,則每次調用同物件的hashCode()時必須總是回傳同樣的整數。但同應用程式的不同執行間不要求同個物件的hashCode()的整數必須相等。

  2. 若兩物件的equals()比較結果相同,則呼叫兩物件的hashCode()必須得到相同結果。

  3. 若兩物件的equals()比較結果不同,則呼叫兩物件的hashCode()不要求(Not required)必須得到不同的結果。
    換句話說,若兩物件的equals()比較結果不同,呼叫兩物件的hashCode()產生的值是有可能相同。此情形稱為hash collisions (雜湊碰撞)。


用更白話一點就是

  1. 同物件的hashCode()必須總是回傳同樣的值。
  2. 若兩物件equals()相等,則hashCode()的值必須相等。
  3. 若兩物件equals()不等,則hashCode()的值不必然不相等。

從程式來看上述解釋。

Car car1 = new Car("BENZ", 100);
Car car2 = new Car("BENZ", 100);
Car car3 = new Car("LEXUS", 100);

System.out.println(car1.hashCode() == car1.hashCode()); // must be true

System.out.println(car1.equals(car2)); // true
System.out.println(car1.hashCode() == car2.hashCode()); // must be true

System.out.println(car1.equals(car3)); // false
System.out.println(car1.hashCode() != car3.hashCode()); // not required must be true

也就是覆寫hashCode()時,必須滿足以上條件,且覆寫equals()必須覆寫hashCode()

根據Object.hashCode()的API說明是以物件的內部記憶體位址來計算出hash值,而Object.hashCode()原始碼如下。

public native int hashCode();

Object.hashCode()是native method,因此實際上Object.hashCode()的實作是依使用的JVM來決定(e.g. HotSpot、OpenJDK)。因此兩相同物件(o1.equals(o2) is true)使用Object.hashCode()產生的hash值可能不同,這就違反上述合約的第二點:「若兩物件相等,則hashCode()的值必須相等。」。

因此沒有覆寫hashCode()則在使用HashMap等利用hash值來存取資料時可能會出現錯誤的行為。例如使用相同物件取不到對應的值。


例如下面為Car未覆寫hashCode()的情況。

Car car1 = new Car("BENZ", 100);
Car car2 = new Car("BENZ", 100);

Map<Car, Integer> map = new HashMap<>();

map.put(car1, 1);
System.out.println(map.get(car2)); // null 

下面則是Car有正確覆寫hashCode()的情況

Car car1 = new Car("BENZ", 100);
Car car2 = new Car("BENZ", 100);

Map<Car, Integer> map = new HashMap<>();

map.put(car1, 1);
System.out.println(map.get(car2)); // 1 

沒有留言:

張貼留言