在java中:
==是運(yùn)算符,用于比較兩個變量是否相等睹簇。
equals奏赘,是Objec類的方法,用于比較兩個對象是否相等太惠,默認(rèn)Object類的equals方法是比較兩個對象的地址磨淌,跟==的結(jié)果一樣。Object的equals方法如下:
public boolean equals(Object obj) {
return (this == obj);
}
hashCode也是Object類的一個方法凿渊。返回一個離散的int型整數(shù)梁只。在集合類操作中使用,為了提高查詢速度埃脏。(HashMap搪锣,HashSet等)
有了這三個基礎(chǔ)概念,區(qū)別就簡單了彩掐。網(wǎng)上有很多构舟,匯總一下:
java中的數(shù)據(jù)類型,可分為兩類:
1.基本數(shù)據(jù)類型堵幽,也稱原始數(shù)據(jù)類型狗超。byte,short,char,int,long,float,double,boolean
他們之間的比較,應(yīng)用雙等號(==),比較的是他們的值朴下。
2.復(fù)合數(shù)據(jù)類型(類)
當(dāng)他們用(==)進(jìn)行比較的時候努咐,比較的是他們在內(nèi)存中的存放地址,所以殴胧,除非是同一個new出來的對象渗稍,他們的比較后的結(jié)果為true,否則比較后結(jié)果為false。 JAVA當(dāng)中所有的類都是繼承于Object這個基類的竿屹,在Object中的基類中定義了一個equals的方法音五,這個方法的初始行為是比較對象的內(nèi)存地 址,但在一些類庫當(dāng)中這個方法被覆蓋掉了羔沙,如String,Integer,Date在這些類當(dāng)中equals有其自身的實(shí)現(xiàn)躺涝,而不再是比較類在堆內(nèi)存中的存放地址了。
對于復(fù)合數(shù)據(jù)類型之間進(jìn)行equals比較扼雏,在沒有覆寫equals方法的情況下坚嗜,他們之間的比較還是基于他們在內(nèi)存中的存放位置的地址值的,因?yàn)镺bject的equals方法也是用雙等號(==)進(jìn)行比較的诗充,所以比較后的結(jié)果跟雙等號(==)的結(jié)果相同苍蔬。
如果兩個對象根據(jù)equals()方法比較是相等的,那么調(diào)用這兩個對象中任意一個對象的hashCode方法都必須產(chǎn)生同樣的整數(shù)結(jié)果蝴蜓。
如果兩個對象根據(jù)equals()方法比較是不相等的碟绑,那么調(diào)用這兩個對象中任意一個對象的hashCode方法,則不一定要產(chǎn)生相同的整數(shù)結(jié)果
從而在集合操作的時候有如下規(guī)則:
將對象放入到集合中時茎匠,首先判斷要放入對象的hashcode值與集合中的任意一個元素的hashcode值是否相等格仲,如果不相等直接將該對象放入集合中。如果hashcode值相等诵冒,然后再通過equals方法判斷要放入對象與集合中的任意一個對象是否相等凯肋,如果equals判斷不相等,直接將該元素放入到集合中汽馋,否則不放入侮东。
回過來說get的時候,HashMap也先調(diào)key.hashCode()算出數(shù)組下標(biāo)豹芯,然后看equals如果是true就是找到了悄雅,所以就涉及了equals。
《Effective Java》書中有兩條是關(guān)于equals和hashCode的:
覆蓋equals時需要遵守的通用約定:
覆蓋equals方法看起來似乎很簡單铁蹈,但是如果覆蓋不當(dāng)會導(dǎo)致錯誤宽闲,并且后果相當(dāng)嚴(yán)重∧痉欤《Effective Java》一書中提到“最容易避免這類問題的辦法就是不覆蓋equals方法”便锨,這句話貌似很搞笑,其實(shí)想想也不無道理我碟,其實(shí)在這種情況下,類的每個實(shí)例都只與它自身相等姚建。如果滿足了以下任何一個條件矫俺,這就正是所期望的結(jié)果:
類的每個實(shí)例本質(zhì)上都是唯一的。對于代表活動實(shí)體而不是值的類來說卻是如此,例如Thread厘托。Object提供的equals實(shí)現(xiàn)對于這些類來說正是正確的行為友雳。
不關(guān)心類是否提供了“邏輯相等”的測試功能。假如Random覆蓋了equals铅匹,以檢查兩個Random實(shí)例是否產(chǎn)生相同的隨機(jī)數(shù)序列押赊,但是設(shè)計者并不認(rèn)為客戶需要或者期望這樣的功能。在這樣的情況下包斑,從Object繼承得到的equals實(shí)現(xiàn)已經(jīng)足夠了流礁。
超類已經(jīng)覆蓋了equals,從超類繼承過來的行為對于子類也是合適的罗丰。大多數(shù)的Set實(shí)現(xiàn)都從AbstractSet繼承equals實(shí)現(xiàn)神帅,List實(shí)現(xiàn)從AbstractList繼承equals實(shí)現(xiàn),Map實(shí)現(xiàn)從AbstractMap繼承equals實(shí)現(xiàn)萌抵。
類是私有的或者是包級私有的找御,可以確定它的equals方法永遠(yuǎn)不會被調(diào)用。在這種情況下绍填,無疑是應(yīng)該覆蓋equals方法的霎桅,以防止它被意外調(diào)用:
@Override
public boolean equals(Object o){
throw new AssertionError(); //Method is never called
}
在覆蓋equals方法的時候,你必須要遵守它的通用約定讨永。下面是約定的內(nèi)容哆档,來自O(shè)bject的規(guī)范[JavaSE6]
自反性。對于任何非null的引用值x住闯,x.equals(x)必須返回true瓜浸。
對稱性。對于任何非null的引用值x和y比原,當(dāng)且僅當(dāng)y.equals(x)返回true時插佛,x.equals(y)必須返回true
傳遞性。對于任何非null的引用值x量窘、y和z雇寇,如果x.equals(y)返回true,并且y.equals(z)也返回true蚌铜,那么x.equals(z)也必須返回true锨侯。
一致性。對于任何非null的引用值x和y冬殃,只要equals的比較操作在對象中所用的信息沒有被修改囚痴,多次調(diào)用該x.equals(y)就會一直地返回true,或者一致地返回false审葬。
對于任何非null的引用值x深滚,x.equals(null)必須返回false奕谭。
結(jié)合以上要求,得出了以下實(shí)現(xiàn)高質(zhì)量equals方法的訣竅:
1.使用==符號檢查“參數(shù)是否為這個對象的引用”痴荐。如果是血柳,則返回true。這只不過是一種性能優(yōu)化生兆,如果比較操作有可能很昂貴难捌,就值得這么做。
2.使用instanceof操作符檢查“參數(shù)是否為正確的類型”鸦难。如果不是根吁,則返回false。一般來說明刷,所謂“正確的類型”是指equals方法所在的那個類婴栽。
3.把參數(shù)轉(zhuǎn)換成正確的類型。因?yàn)檗D(zhuǎn)換之前進(jìn)行過instanceof測試辈末,所以確保會成功愚争。
4.對于該類中的每個“關(guān)鍵”域,檢查參數(shù)中的域是否與該對象中對應(yīng)的域相匹配挤聘。如果這些測試全部成功轰枝,則返回true;否則返回false。
5.當(dāng)編寫完成了equals方法之后组去,檢查“對稱性”鞍陨、“傳遞性”、“一致性”从隆。
覆蓋equals時總要覆蓋hashCode
一個很常見的錯誤根源在于沒有覆蓋hashCode方法诚撵。在每個覆蓋了equals方法的類中,也必須覆蓋hashCode方法键闺。如果不這樣做的話寿烟,就會違反Object.hashCode的通用約定,從而導(dǎo)致該類無法結(jié)合所有基于散列的集合一起正常運(yùn)作辛燥,這樣的集合包括HashMap筛武、HashSet和Hashtable。
在應(yīng)用程序的執(zhí)行期間挎塌,只要對象的equals方法的比較操作所用到的信息沒有被修改徘六,那么對這同一個對象調(diào)用多次,hashCode方法都必須始終如一地返回同一個整數(shù)榴都。在同一個應(yīng)用程序的多次執(zhí)行過程中待锈,每次執(zhí)行所返回的整數(shù)可以不一致。
如果兩個對象根據(jù)equals()方法比較是相等的缭贡,那么調(diào)用這兩個對象中任意一個對象的hashCode方法都必須產(chǎn)生同樣的整數(shù)結(jié)果炉擅。
如果兩個對象根據(jù)equals()方法比較是不相等的辉懒,那么調(diào)用這兩個對象中任意一個對象的hashCode方法阳惹,則不一定要產(chǎn)生相同的整數(shù)結(jié)果谍失。但是程序員應(yīng)該知道,給不相等的對象產(chǎn)生截然不同的整數(shù)結(jié)果莹汤,有可能提高散列表的性能快鱼。