本篇筆記Object.hashCode()的理解。
Object.hashCode()用來支援HashMap,HashSet等需要計算該物件的hash值(雜湊值)做為存放位置的索引(bucket number)或比較彼此不同的情況。
Object.hashCode()合約規範(general contract of hashCode)如下。
在一個Java應用程式執行中,若
equals()中用來比較的資訊(通常是物件的屬性的hashCode()值)沒被修改,則每次調用同物件的hashCode()時必須總是回傳同樣的整數。但同應用程式的不同執行間不要求同個物件的hashCode()的整數必須相等。若兩物件的
equals()比較結果相同,則呼叫兩物件的hashCode()必須得到相同結果。若兩物件的
equals()比較結果不同,則呼叫兩物件的hashCode()不要求(Not required)必須得到不同的結果。
換句話說,若兩物件的equals()比較結果不同,呼叫兩物件的hashCode()產生的值是有可能相同。此情形稱為hash collisions (雜湊碰撞)。
用更白話一點就是
- 同物件的
hashCode()必須總是回傳同樣的值。 - 若兩物件
equals()相等,則hashCode()的值必須相等。 - 若兩物件
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
沒有留言:
張貼留言