這篇文章主要講解了“怎么重寫Equals方法”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“怎么重寫Equals方法”吧!
創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比祁東網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式祁東網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋祁東地區(qū)。費(fèi)用合理售后完善,10余年實(shí)體公司更值得信賴。
其實(shí)每個(gè)類都有一個(gè)equals方法和hashcode方法。因?yàn)樗械念惗祭^承自O(shè)bject類。Object類中定義如下:
public boolean equals(Object obj) { return (this == obj); } public native int hashCode();
直觀上可以看到equals方法默認(rèn)比較的是對(duì)象的引用,直接用“==”進(jìn)行比較。而hashCode方法是一個(gè)native方法,返回值為整型。
而這兩個(gè)方法都未被final修飾,都是可以進(jìn)行重寫的。
對(duì)于我們經(jīng)常使用的比如String 、Math、Integer、Double等類,都進(jìn)行了equals()和hashCode()方法的重寫。
equals()方法是用來(lái)判斷兩個(gè)對(duì)象是否相等。Object默認(rèn)實(shí)現(xiàn)了equals方法,但很明顯不太符合個(gè)性化的需求,因此往往需要進(jìn)行重寫。比如常用的String類,重寫的equals方法如下:
// 重寫equals方法 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
這里的比較已不再是單純的地址比較了。首先通過(guò)地址進(jìn)行比較,如果地址相同那么肯定是相同的對(duì)象。如果地址不同就再拿char數(shù)組的內(nèi)容進(jìn)行比較,完全相等則返回true。
在Object類的equals方法上有注釋說(shuō)明了equals()方法需滿足的一些特性:
自反性(reflexive)。對(duì)于任意不為null的引用值x,x.equals(x)一定是true;
對(duì)稱性(symmetric)。對(duì)于任意不為null的引用值x和y,當(dāng)且僅當(dāng)x.equals(y)是true時(shí),y.equals(x)也是true;
傳遞性(transitive)。對(duì)于任意不為null的引用值x、y和z,如果x.equals(y)是true,同時(shí)y.equals(z)是true,那么x.equals(z)一定是true;
一致性(consistent)。對(duì)于任意不為null的引用值x和y,如果用于equals比較的對(duì)象信息沒有被修改的話,多次調(diào)用時(shí)x.equals(y)要么一致地返回true要么一致地返回false;
對(duì)于任意不為null的引用值x,x.equals(null)返回false;
對(duì)照上面特質(zhì),我們發(fā)現(xiàn)Object方法直接比較的是兩個(gè)引用地址,只有兩個(gè)地址相同才相等,也就是說(shuō)是差別可能性最大的等價(jià)關(guān)系。
而String的equals方法,不僅包含應(yīng)用地址相同這種情況,還包括里面所存儲(chǔ)的字符串值相同的情況。也就是說(shuō)雖然是兩個(gè)String對(duì)象,但是它們的字符串值相等,那么equals方法返回的結(jié)果就是true。這也正是大多數(shù)情況下我們所說(shuō)的“equals方法比較的是值”。
由于Object的equals方法的默認(rèn)特例存在,因此在沒有自定義equals方法時(shí),我們不能一概的說(shuō)equals方法比較的是具體的值,而“==”比較的是引用。
hashCode()方法返回對(duì)象的一個(gè)hash code值。該方法被用于hash tables,如HashSet、HashMap。
hashCode()是一個(gè)native方法,返回值類型是整形,并且可以被重寫。
Object中的native hashCode()方法將對(duì)象在內(nèi)存中的地址作為哈希碼返回,可以保證不同對(duì)象的返回值不同。
還以String類為例,它的hashCode方法為:
// 重寫hashCode方法 public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
上述hash值的計(jì)算注釋中有說(shuō)明,基本公式為:s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]。
其中, s[i]是字符串的第i個(gè)字符,n是字符串的長(zhǎng)度,^表示求冪(空字符串的哈希碼為0)。
計(jì)算過(guò)程中使用數(shù)字31,主要有以下原因:
1、由于質(zhì)數(shù)的特性,它與其他數(shù)字相乘之后,計(jì)算結(jié)果唯一的概率更大,哈希沖突的概率更小。
2、使用的質(zhì)數(shù)越大,哈希沖突的概率越小,但是計(jì)算的速度也越慢;31是哈希沖突和性能的折中,實(shí)際上是實(shí)驗(yàn)觀測(cè)的結(jié)果。
3、JVM會(huì)自動(dòng)對(duì)31進(jìn)行優(yōu)化:31 * i == (i << 5) - i;
前面提到hashCode()方法主要用于hash表中,比如HashSet、HashMap等。
我們先來(lái)看一下ArrayList,它的底層是數(shù)組,每個(gè)數(shù)據(jù)往底層的數(shù)組中存取即可,數(shù)據(jù)不需要判斷是否重復(fù)。
集合Set中的元素是無(wú)序不可重復(fù)的,那么如何確保存入的元素不重復(fù)呢?逐個(gè)調(diào)用equals()方法進(jìn)行比較?數(shù)據(jù)量少的時(shí)候還可以,但數(shù)據(jù)量大了時(shí)間復(fù)雜度基本上是O(n),會(huì)出現(xiàn)性能問(wèn)題。
Java中采用哈希算法來(lái)解決這個(gè)問(wèn)題,將對(duì)象(或數(shù)據(jù))依特定算法直接映射到一個(gè)地址上,這樣時(shí)間復(fù)雜度趨于O(1),對(duì)象的存取效率大大提高。
集合Set添加某元素時(shí),先調(diào)用hashCode()方法,定位到此元素實(shí)際存儲(chǔ)位置,如果這個(gè)位置沒有元素,說(shuō)明是第一次存儲(chǔ);若此位置有對(duì)象存在,調(diào)用equals()進(jìn)行比較,相等就舍棄此元素不存,不等則散列到其他地址。
上面的示例也說(shuō)明了為什么equals()相等,則hashCode()必須相等,進(jìn)而當(dāng)重寫了equals方法,也要對(duì)hashCode()方法進(jìn)行重寫。
HashMap的基本處理機(jī)制與HashSet很類似,只不過(guò)底層的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)有所不同而已。
簡(jiǎn)而言之,在集合查找時(shí),hashcode能極大的降低對(duì)象比較次數(shù),提高查找效率。
hashCode的實(shí)現(xiàn)也有一定的要求,相關(guān)英文說(shuō)明在Object的equals方法注解上:
在一個(gè)Java應(yīng)用的執(zhí)行期間,如果一個(gè)對(duì)象提供給equals做比較的信息沒有被修改的話,該對(duì)象多次調(diào)用hashCode()方法,該方法必須始終如一返回同一個(gè)integer。
如果兩個(gè)對(duì)象根據(jù)equals(Object)方法是相等的,那么調(diào)用二者各自的hashCode()方法必須產(chǎn)生同一個(gè)integer結(jié)果。
并不要求根據(jù)equals(java.lang.Object)方法不相等的兩個(gè)對(duì)象,調(diào)用二者各自的hashCode()方法必須產(chǎn)生不同的integer結(jié)果。但對(duì)于不同的對(duì)象產(chǎn)生不同的integer結(jié)果,有可能會(huì)提高h(yuǎn)ash table的性能。
《Effective Java》中提供了一種簡(jiǎn)單通用的hashCode算法。
A、初始化一個(gè)整形變量,為此變量賦予一個(gè)非零的常數(shù)值,比如int result = 17;
B、選取equals方法中用于比較的所有域(之所以只選擇equals()中使用的域,是為了保證上述原則的第1條),然后針對(duì)每個(gè)域的屬性進(jìn)行計(jì)算:
(1) 如果是boolean值,則計(jì)算f ? 1:0
(2) 如果是byte\char\short\int,則計(jì)算(int)f
(3) 如果是long值,則計(jì)算(int)(f ^ (f >>> 32))
(4) 如果是float值,則計(jì)算Float.floatToIntBits(f)
(5) 如果是double值,則計(jì)算Double.doubleToLongBits(f),然后返回的結(jié)果是long,再用規(guī)則(3)去處理long,得到int
(6) 如果是對(duì)象應(yīng)用,如果equals方法中采取遞歸調(diào)用的比較方式,那么hashCode中同樣采取遞歸調(diào)用hashCode的方式。否則需要為這個(gè)域計(jì)算一個(gè)范式,比如當(dāng)這個(gè)域的值為null的時(shí)候,那么hashCode 值為0
(7) 如果是數(shù)組,那么需要為每個(gè)元素當(dāng)做單獨(dú)的域來(lái)處理。java.util.Arrays.hashCode方法包含了8種基本類型數(shù)組和引用數(shù)組的hashCode計(jì)算,算法同上。
C、最后,把每個(gè)域的散列碼合并到對(duì)象的哈希碼中。
感謝各位的閱讀,以上就是“怎么重寫Equals方法”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)怎么重寫Equals方法這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
當(dāng)前標(biāo)題:怎么重寫Equals方法
分享路徑:http://chinadenli.net/article18/poddgp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、品牌網(wǎng)站建設(shè)、動(dòng)態(tài)網(wǎng)站、靜態(tài)網(wǎng)站、Google、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)