本篇筆記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
沒有留言:
張貼留言