在Java語言中,equals()和hashCode()兩個函數(shù)的使用是緊密配合的,你要是自己設(shè)計其中一個,就要設(shè)計另外一個。在多數(shù)情況 下,這兩個函數(shù)是不用考慮的,直接使用它們的默認(rèn)設(shè)計就可以了。但是在一些情況下,這兩個函數(shù)最好是自己設(shè)計,才能確保整個程序的正常運行。最常見的是當(dāng) 一個對象被加入收集對象(collection object)時,這兩個函數(shù)必須自己設(shè)計。更細(xì)化的定義是:如果你想將一個對象A放入另一個收集對象B里,或者使用這個對象A為查找一個元對象在收集對 象B里位置的鑰匙,并支持是否容納,刪除收集對象B里的元對象這樣的操作,那么,equals()和hashCode()函數(shù)必須開發(fā)者自己定義。其他情 況下,這兩個函數(shù)是不需要定義的。
equals():它是用于進(jìn)行兩個對象的比較的,是對象內(nèi)容的比 較,當(dāng)然也能用于進(jìn)行對象參閱值的比較。什么是對象參閱值的比較?就是兩個參閱變量的值得比較,我們 都知道參閱變量的值其實就是一個數(shù)字,這個數(shù)字可以看成是鑒別不同對象的代號。兩個對象參閱值的比較,就是兩個數(shù)字的比較,兩個代號的比較。這種比較是默 認(rèn)的對象比較方式,在Object這個對象中,這種方式就已經(jīng)設(shè)計好了。所以你也不用自己來重寫,浪費不必要的時間。
對象內(nèi)容的比較才是設(shè)計equals()的真正目的,Java語言對equals()的要求如下,這些要求是必須遵循的。否則,你就不該浪費時間:- 對稱性:如果x.equals(y)返回是“true”,那么y.equals(x)也應(yīng)該返回是“true”。
- 反射性:x.equals(x)必須返回是“true”。
- 類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也應(yīng)該返回是“true”。
- 還有一致性:如果x.equals(y)返回是“true”,只要x和y內(nèi)容一直不變,不管你重復(fù)x.equals(y)多少次,返回都是“true”。
- 任何情況下,x.equals(null),永遠(yuǎn)返回是“false”;x.equals(和x不同類型的對象)永遠(yuǎn)返回是“false”。
這 個函數(shù)返回的就是一個用來進(jìn)行赫希操作的整型代號,請不要把這個代號和前面所說的參閱變量所代表的代號弄混了。后者不僅僅是個代號還具有在內(nèi)存中才查找對 象的位置的功能。hashCode()所返回的值是用來分類對象在一些特定的收集對象中的位置。這些對象是HashMap, Hashtable, HashSet,等等。這個函數(shù)和上面的equals()函數(shù)必須自己設(shè)計,用來協(xié)助HashMap, Hashtable, HashSet,等等對自己所收集的大量對象進(jìn)行搜尋和定位。
這些收集對象究竟如何工作的,想象每個元對象hashCode是一個箱子的 編碼,按照編碼,每個元對象就是根據(jù)hashCode()提供的代號歸入相應(yīng)的箱子里。所有的箱子加起來就是一個HashSet,HashMap,或 Hashtable對象,我們需要尋找一個元對象時,先看它的代碼,就是hashCode()返回的整型值,這樣我們找到它所在的箱子,然后在箱子里,每 個元對象都拿出來一個個和我們要找的對象進(jìn)行對比,如果兩個對象的內(nèi)容相等,我們的搜尋也就結(jié)束。這種操作需要兩個重要的信息,一是對象的 hashCode(),還有一個是對象內(nèi)容對比的結(jié)果。
hashCode()的返回值和equals()的關(guān)系如下:
- 如果x.equals(y)返回“true”,那么x和y的hashCode()必須相等。
- 如果x.equals(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不等。
為
什么這兩個規(guī)則是這樣的,原因其實很簡單,拿HashSet來說吧,HashSet可以擁有一個或更多的箱子,在同一個箱子中可以有一個
或更多的獨特元對象(HashSet所容納的必須是獨特的元對象)。這個例子說明一個元對象可以和其他不同的元對象擁有相同的hashCode。但是一個
元對象只能和擁有同樣內(nèi)容的元對象相等。所以這兩個規(guī)則必須成立。
設(shè)計這兩個函數(shù)所要注意到的:
如果你設(shè)計的對象類型并不使用于收集性對象,那么沒有必要自己再設(shè)計這兩個函數(shù)的處理方式。這是正確的面向?qū)ο笤O(shè)計方法,任何用戶一時用不到的功能,就先不要設(shè)計,以免給日后功能擴展帶來麻煩。
如果你在設(shè)計時想別出心裁,不遵守以上的兩套規(guī)則,那么勸你還是不要做這樣想入非非的事。我還沒有遇到過哪一個開發(fā)者和我說設(shè)計這兩個函數(shù)要違背前面說的兩個規(guī)則,我碰到這些違反規(guī)則的情況時,都是作為設(shè)計錯誤處理。
當(dāng) 一個對象類型作為收集型對象的元對象時,這個對象應(yīng)該擁有自己處理equals(),和/或處理hashCode()的設(shè)計,而且要遵守前面所說 的兩種原則。equals()先要查null和是否是同一類型。查同一類型是為了避免出現(xiàn)ClassCastException這樣的異常給丟出來。查 null是為了避免出現(xiàn)NullPointerException這樣的異常給丟出來。
如果你的對象里面容納的數(shù)據(jù)過多,那么這兩個函數(shù) equals()和hashCode()將會變得效率低。如果對象中擁有無法serialized的數(shù)據(jù),equals()有可能在操作中出現(xiàn)錯誤。想象 一個對象x,它的一個整型數(shù)據(jù)是transient型(不能被serialize成二進(jìn)制數(shù)據(jù)流)。然而equals()和hashCode()都有依靠 這個整型數(shù)據(jù),那么,這個對象在serialization之前和之后,是否一樣?答案是不一樣。因為serialization之前的整型數(shù)據(jù)是有效的 數(shù)據(jù),在serialization之后,這個整型數(shù)據(jù)的值并沒有存儲下來,再重新由二進(jìn)制數(shù)據(jù)流轉(zhuǎn)換成對象后,兩者(對象在serialization 之前和之后)的狀態(tài)已經(jīng)不同了。這也是要注意的。原文 :http://blog.csdn.net/RichardSundusky/archive/2007/02/12/1508028.aspx