Java中的equals和hashCode方法
引言在Java中,、equals()和hashCode()都和對(duì)象的比較有關(guān)。但是為什么需要設(shè)計(jì)這三種對(duì)象的比較方法呢?本文將解釋它們的用途。操作符操作符用于判斷兩個(gè)對(duì)象是否是同一個(gè)對(duì)象。對(duì)于引用變量
引言
在Java中,、equals()和hashCode()都和對(duì)象的比較有關(guān)。但是為什么需要設(shè)計(jì)這三種對(duì)象的比較方法呢?本文將解釋它們的用途。
操作符
操作符用于判斷兩個(gè)對(duì)象是否是同一個(gè)對(duì)象。對(duì)于引用變量而言,比較的是兩個(gè)引用變量引用的是不是同一個(gè)對(duì)象,即比較的是兩個(gè)引用中存儲(chǔ)的對(duì)象地址是不是一樣的。對(duì)于基本數(shù)據(jù)類型而言,比較的是兩個(gè)數(shù)據(jù)是不是相等。由于基本數(shù)據(jù)類型沒有方法,所以不存在equals()和hashCode()的問題,下面的討論都是針對(duì)引用類型而言的。
equals()方法
為什么Java會(huì)設(shè)計(jì)equals()方法?操作符只能判斷兩個(gè)對(duì)象是否是同一個(gè)對(duì)象,這并不能滿足很多需求。有時(shí)候當(dāng)兩個(gè)對(duì)象不是同一個(gè)對(duì)象的時(shí)候,我們?nèi)匀粫?huì)認(rèn)為兩者是“相等”的,比如對(duì)于String對(duì)象,當(dāng)兩個(gè)對(duì)象的字符串序列是一致的,我們就認(rèn)為他們是“相等”的。對(duì)于有這種需求的對(duì)象的類,需要重寫其equals()方法來實(shí)現(xiàn)具體的“相等”邏輯。需要注意的是,Object類中的默認(rèn)實(shí)現(xiàn)是比較兩個(gè)對(duì)象是不是同一個(gè)對(duì)象,即其和操作符的效果是相同的。Java提供的某些類已經(jīng)重寫了equals()方法,自己編寫的類如果需要實(shí)現(xiàn)自己的“相等”邏輯,也需要重寫equals()方法。
hashCode()方法
為什么會(huì)設(shè)計(jì)hashCode()方法?hashCode()方法返回的是一個(gè)數(shù)值,我們稱之為hashCode。從方法的名稱上就可以看出,其目的是生成一個(gè)hash碼。hash碼的主要用途就是在對(duì)對(duì)象進(jìn)行散列的時(shí)候作為key輸入,所以每個(gè)對(duì)象的hash碼盡可能不同,這樣才能保證散列的存取性能。Object類提供的默認(rèn)實(shí)現(xiàn)確保每個(gè)對(duì)象的hash碼不同(在對(duì)象的內(nèi)存地址基礎(chǔ)上經(jīng)過特定算法返回一個(gè)hash碼)。根據(jù)Java規(guī)范,hashCode()方法和equals()方法可以沒有關(guān)系。
equals()和hashCode()的關(guān)系
對(duì)于集合類HashSet、HashMap等和hash有關(guān)的類,是通過hash算法來散列對(duì)象的。對(duì)HashSet而言,存入對(duì)象的流程為:根據(jù)對(duì)象的hash碼,經(jīng)過hash算法,找到對(duì)象應(yīng)該存放的位置,如果該位置為空,則將對(duì)象存入該位置;如果該位置不為空,則使用equals()比較該位置的對(duì)象和將要入的對(duì)象,如果兩個(gè)相等,則不再插入,如果不相等,則根據(jù)hash沖突解決算法將對(duì)象插入其他位置。Java規(guī)定對(duì)于HashSet判斷是不是重復(fù)對(duì)象就是通過equals()方法來完成,這就需要在兩個(gè)對(duì)象equals()方法相等的時(shí)候,hash碼一定相等(即hashCode()返回的值相等)。假設(shè)兩個(gè)對(duì)象equals()方法相等的時(shí)候,hash碼不相等,會(huì)出現(xiàn)equals()相等的兩個(gè)對(duì)象都插入了HashSet中,這是不允許的。因此我們有以下結(jié)論:對(duì)于equals()相等的兩個(gè)對(duì)象,其hashCode()返回的值一定相等。
如何設(shè)計(jì)hashCode()方法
為了保證“對(duì)于equals()相等的對(duì)象hashCode()要一定相等”,在設(shè)計(jì)hashCode()方法時(shí),可以從對(duì)象的各個(gè)關(guān)鍵域中選取部分或全部字段,通過特定算法得到一個(gè)hash碼。同樣地,對(duì)于equals()不同的對(duì)象,應(yīng)該盡量保證hash碼不同。一個(gè)常用的算法如下:
1. 把某個(gè)非零常數(shù)值(一般取素?cái)?shù))17保存在int變量result中。
2. 對(duì)于對(duì)象中的每一個(gè)關(guān)鍵域f:
- 如果f是boolean型,計(jì)算(f ? 0 : 1)。
- 如果f是byte、char、short型,計(jì)算(int)f。
- 如果f是long型,計(jì)算(int) (f ^ (f >>> 32))。
- 如果f是float型,計(jì)算Float.floatToIntBits(afloat)。
- 如果f是double型,計(jì)算(adouble)得到一個(gè)long,再執(zhí)行上一步。
- 如果f是對(duì)象引用,遞歸調(diào)用它的hashCode()方法。
- 如果f是數(shù)組域,對(duì)其中每個(gè)元素調(diào)用它的hashCode()方法。
3. 將上面計(jì)算得到的散列碼保存到int變量c,然后執(zhí)行result 37 * result c。
4. 返回result。
通過這種算法,可以保證“對(duì)于equals()相等的對(duì)象hashCode()返回的值一定相等”,并且盡可能使不同的對(duì)象產(chǎn)生不同的hash碼。
通過以上分析,我們可以得出equals()和hashCode()在Java中的使用和設(shè)計(jì)原則。對(duì)于需要進(jìn)行對(duì)象比較的類,應(yīng)該重寫equals()方法來實(shí)現(xiàn)自定義的“相等”邏輯,并同時(shí)重寫hashCode()方法,以滿足equals()和hashCode()的關(guān)聯(lián)要求。這樣可以確保在使用HashSet等集合類時(shí),對(duì)象的比較和散列的正確性。