很多人可能都知道,在每個覆蓋了equals
方法的類中铐达,也必須覆蓋hashCode
方法岖赋;但是這里面的原因是什么呢?
我想應(yīng)該從兩個方面闡述這個問題:
- 什么情況下需要覆蓋
equals
瓮孙? - 在覆蓋了
equals
的同時未覆蓋hashCode
會導(dǎo)致什么問題?
第一個問題选脊,什么情況下需要覆蓋equals
呢杭抠?首先我們需要知道,如果不覆蓋equals
恳啥,由于每個類的實(shí)例在內(nèi)存中都是唯一的偏灿,那么就無法做到兩個業(yè)務(wù)上等同的實(shí)例equals
也返回true
,所以說覆蓋equals
一般都是業(yè)務(wù)需要钝的,即有些場景下只需要對象的關(guān)鍵屬性相等翁垂,就認(rèn)為他們相等
其次,如果在覆蓋了equals
的同時未覆蓋hashCode
會導(dǎo)致什么問題呢硝桩?這個問題的答案在于:如果沒有遵守覆蓋equals
時同時覆蓋hashCode
沿猜,就會違反Object.hashCode
的通用約定(具體的約定大家可以至Obejct
類hashCode
方法注釋處查看),從而導(dǎo)致該類無法結(jié)合所有基于散列的集合一起正常工作碗脊,這樣的集合包括HashMap
啼肩,HashSet
和HashTable
,有點(diǎn)難理解是嗎衙伶?沒關(guān)系祈坠,舉例說明就清楚了,假設(shè)有一個PhoneNumber
類矢劲,只需兩個實(shí)例的prefix
和lineNumber
屬性相等赦拘,即可認(rèn)為兩個PhoneNumber
相等:
public class PhoneNumber {
private short areaCode;
private short prefix;//關(guān)鍵屬性
private short lineNumber;//關(guān)鍵屬性
public PhoneNumber(short areaCode, short prefix, short lineNumber) {
this.areaCode = areaCode;
this.prefix = prefix;
this.lineNumber = lineNumber;
}
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(!(o instanceof PhoneNumber)) {
return false;
}
PhoneNumber compareO = (PhoneNumber)o;
return compareO.prefix == this.prefix
&& compareO.lineNumber == this.lineNumber;
}
}
然后將PhoneNumber
與HashMap
一起使用:
Map<PhoneNumber, String> m = new HashMap<>();
m.put(new PhoneNumber(21, 37, 3245), "xiaobai");
System.out.println(m.get(new PhoneNumber(53, 37, 3245)));//輸出:null
你可能期望輸出的是:"xiaobai",因?yàn)?code>put和get
的兩個key對象的equals
返回是true芬沉,但實(shí)際上得到的卻是null
躺同,為什么呢?原因就在于未覆蓋hashCode
花嘶,兩個對象即便相等笋籽,但是其hashCode
還是可能不等,那么put
方法把PhoneNumber
對象存放至一個散列桶(hash bucket)中椭员,而get
方法卻在另外一個散列桶中查找這個對象车海,當(dāng)然找不到,退一步說,即便是兩個實(shí)例剛好被放至于同一個散列桶中侍芝,get
方法依然還是會返回null
研铆,因?yàn)镠ashMap有一項(xiàng)機(jī)制:如果兩個實(shí)例散列碼不匹配, 直接放棄比較其等同性
我想州叠,講到這里大家應(yīng)該清楚了為什么覆蓋了equals方法的同時必須覆蓋hashCode的原因所在棵红,但有人可能會說,我這個對象不會和基于散列的集合一起使用的咧栗,所以不需要遵守這個約定逆甜,但正所謂魔鬼隱藏在細(xì)節(jié)之中,你現(xiàn)在不會使用不代表以后不會使用(不要相信自己的記憶)致板;你知道不能一起使用交煞,不代表別人知道(即便有注釋別人也不一定會看)。所以建議一般情況下都遵守這項(xiàng)約定斟或,只是稍微增加一點(diǎn)工作量而已素征,卻是代碼健壯的基石