彻底搞懂为什么重写equals还要重写hashcode?
引言
原文鏈接:深入理解equals和hashCode?由于hashCode與HashMap有一定關(guān)系,推薦大家看一下我的這篇文章?HashMap源碼大剖析?
?
本文介紹java.lang.Object類中的兩個(gè)方法:equals和hashCode。這兩個(gè)方法大家應(yīng)該都知道,但是這兩個(gè)方法的作用是什么、為什么重寫(xiě)equals還要重寫(xiě)hashCode、它們之間有什么關(guān)系和約定等,或許有些小伙伴還不是很清楚,知其然知其所以然,今天就來(lái)帶大家了解一下。
百度搜索"為什么重寫(xiě)equals一定要重寫(xiě)hashcode",第二條結(jié)果就是我寫(xiě)的文章。這是我很久之前寫(xiě)的,其中的內(nèi)容表述得不太清楚,或多或少難以讓人信服,于是我決定再寫(xiě)一篇。
1、hashCode
在閱讀下面的內(nèi)容之前,大家可能需要先了解一下什么是哈希表、哈希函數(shù),這是數(shù)據(jù)結(jié)構(gòu)相關(guān)的知識(shí)。
hashCode即散列碼。散列碼是用一個(gè)int值來(lái)代表對(duì)象,它是通過(guò)將該對(duì)象的某些信息進(jìn)行轉(zhuǎn)換而生成的。
Object類中默認(rèn)的hashCode方法如下。
public?native?int?hashCode();這是一個(gè)本地方法,不同的虛擬機(jī)有不同的實(shí)現(xiàn)(具體實(shí)現(xiàn)自己看虛擬機(jī)源碼哈)。Object默認(rèn)的hashCode是根據(jù)對(duì)象的內(nèi)存地址轉(zhuǎn)化而來(lái)的,它是唯一的。
我們可以在自己的類中覆蓋hashCode方法,但我們可以使用System.identityHashCode(Object x)方法返回默認(rèn)的hashcode,無(wú)論對(duì)象是否覆蓋默認(rèn)的hashcode。
hashCode方法主要是為了給諸如HashMap這樣的哈希表使用。
設(shè)計(jì)hashcode最重要的因素是:對(duì)同一個(gè)對(duì)象調(diào)用hachCode()應(yīng)該產(chǎn)生同樣的值(前提是對(duì)象的信息沒(méi)有被改變)。
設(shè)計(jì)一個(gè)hashCode,它必須快,而且具有意義(使用有意義的字段來(lái)生成hashcode)。hashCode不需要唯一(默認(rèn)的hashCode唯一),因此更應(yīng)該關(guān)注它的速度,而不是唯一性。
由于在生成桶(桶指哈希桶,或哈希表的槽位)的下標(biāo)前,hashcode還要做進(jìn)一步處理,所以生成的hashCode范圍不是很重要,是int就行。
好的hashCode()應(yīng)該產(chǎn)生分布均勻的散列碼。
哈希桶的大小最好是2的n次方。
-
對(duì)現(xiàn)代處理器來(lái)說(shuō),除數(shù)和求余是最慢的操作,而使用2的n次方,可以用位運(yùn)算代替求余(%開(kāi)銷較大)。
-
舉個(gè)例子,假設(shè)哈希桶大小為16(HashMap初始大小),假設(shè)hashCode為20,那么使用%求余會(huì)得到下標(biāo)4;但這可以用hashCode&(length-1)代替,即20&(16-1),結(jié)果也是4。
2、equals
hashCode并不需要唯一性,但equals必須嚴(yán)格地判斷兩個(gè)對(duì)象是否相同。
正確的equals方法有如下特性:
-
自反性:x.equals(x)一定返回true
-
對(duì)稱性:如果x.equals(y)為true,那么y.equals(x)也為true
-
傳遞性:如果x.equals(y)為true、y.equals(z)為true,那么x.equals(z)也為true
-
一致性:如果x和y中用于等價(jià)比較的信息沒(méi)有改變,那么x.equals(y)無(wú)論調(diào)用多少次,結(jié)果都一致
-
任何不是null的x,x.equals(null)一定返回false
3、equals與hashCode的相關(guān)規(guī)定
之所以有規(guī)定,是為了使諸如HashMap這樣的哈希表正常使用。具體規(guī)定如下:
equals相等,hashcode一定相等。
equals不等,hashcode不一定不等。
hashcode不等,equals一定不等。
hashcode相等,equals不一定相等。
因此,如果我們重寫(xiě)了equals,那么必須重寫(xiě)hashCode,使其滿足這些規(guī)定。當(dāng)然,如果我們不把自定義對(duì)象當(dāng)成HashMap的鍵來(lái)使用,那么自定義對(duì)象不重寫(xiě)equals和hashCode也是可以的。
下面來(lái)詳細(xì)解釋一下,為什么這些規(guī)定能讓HashMap正常工作。
3.1、equals相等,hashCode一定相等
因?yàn)镠ashMap是用equals判斷鍵是否相等的,用反證法,如果兩個(gè)鍵 equals相等,而hashcode不等的話,那么就無(wú)法保證通過(guò)hashcode計(jì)算的下標(biāo)值相等,下標(biāo)值不等也就意味著相等的兩個(gè)鍵卻 到了不同的值,這肯定是不對(duì)的。
3.2、equals不等,hashcode不一定不等
equals不等,一般hashcode也不相等,這是為了盡量減少哈希沖突。但為啥會(huì)出現(xiàn)相等的情況呢,因?yàn)閔ashcode是int類型,是有范圍的,當(dāng)數(shù)據(jù)量很大的情況下,難免會(huì)發(fā)生沖突,此時(shí)HashMap通過(guò)拉鏈法解決沖突。
當(dāng)從map中獲取equals不等的兩個(gè)鍵時(shí),由于它們的hashcode相同,所以計(jì)算到的下標(biāo)值也相同,當(dāng)定位到同一個(gè)桶位時(shí),會(huì)在單鏈表上順序查找,查找到的依據(jù)就是要查找的鍵與單鏈表上的鍵equals相等。
3.3、hashcode不等,equals一定不等
這個(gè)相當(dāng)于規(guī)定1的逆反命題。
3.4、hashcode相等,equals不一定相等
這個(gè)相當(dāng)于規(guī)定2的逆反命題。如果兩個(gè)鍵hashcode相等,那么計(jì)算到的下標(biāo)值是相同的,這時(shí)候兩個(gè)鍵可能是相等的(該桶位有一個(gè)元素),也可能是不等的(該桶位有兩個(gè)元素)。
總結(jié)
以上是生活随笔為你收集整理的彻底搞懂为什么重写equals还要重写hashcode?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 移动互联网思维的5F法则
- 下一篇: jsoup填充内容然后html转word