Hashcode與equals

  1. 首先equals()和hashcode()這兩個(gè)方法都是從object類中繼承過(guò)來(lái)的。
equals()方法在object類中定義如下: 
  public boolean equals(Object obj) { 
return (this == obj); 
} 

很明顯是對(duì)兩個(gè)對(duì)象的地址值進(jìn)行的比較(即比較引用是否相同)文留。但是我們必需清楚胜臊,當(dāng)String 该押、Math问麸、還有Integer往衷、Double。严卖。席舍。。等這些封裝類在使用equals()方法時(shí)妄田,已經(jīng)覆蓋了object類的equals()方法。

比如在String類中如下: 
  public boolean equals(Object anObject) { 
if (this == anObject) { 
    return true; 
} 
if (anObject instanceof String) { 
    String anotherString = (String)anObject; 
    int n = count; 
    if (n == anotherString.count) { 
char v1[] = value; 
char v2[] = anotherString.value; 
int i = offset; 
int j = anotherString.offset; 
while (n-- != 0) { 
    if (v1[i++] != v2[j++]) 
return false; 
} 
return true; 
    } 
} 
return false; 
} 

很明顯驮捍,這是進(jìn)行的內(nèi)容比較疟呐,而已經(jīng)不再是地址的比較。依次類推Double东且、Integer启具、Math。珊泳。鲁冯。。等等這些類都是重寫了equals()方法的色查,從而進(jìn)行的是內(nèi)容的比較薯演。當(dāng)然了基本類型是進(jìn)行值的比較,這個(gè)沒(méi)有什么好說(shuō)的秧了。
我們還應(yīng)該注意跨扮,Java語(yǔ)言對(duì)equals()的要求如下,這些要求是必須遵循的:
? 對(duì)稱性:如果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不同類型的對(duì)象)永遠(yuǎn)返回是“false”尊剔。
以上這五點(diǎn)是重寫equals()方法時(shí)爪幻,必須遵守的準(zhǔn)則,如果違反會(huì)出現(xiàn)意想不到的結(jié)果须误,請(qǐng)大家一定要遵守挨稿。

  1. 其次是hashcode() 方法,在object類中定義如下:
    public native int hashCode();
    說(shuō)明是一個(gè)本地方法京痢,它的實(shí)現(xiàn)是根據(jù)本地機(jī)器相關(guān)的奶甘。當(dāng)然我們可以在自己寫的類中覆蓋hashcode()方法,比如String祭椰、Integer臭家、Double。方淤。钉赁。逢防。等等這些類都是覆蓋了hashcode()方法的毛雇。
例如在String類中定義的hashcode()方法如下: 
    public int hashCode() { 
int h = hash; 
if (h == 0) { 
    int off = offset; 
    char val[] = value; 
    int len = count; 

            for (int i = 0; i < len; i++) { 
                h = 31*h + val[off++]; 
            } 
            hash = h; 
        } 
        return h; 
} 

解釋一下這個(gè)程序(String的API中寫到):
s[0]31^(n-1) + s[1]31^(n-2) + ... + s[n-1]
使用 int 算法轮锥,這里 s[i] 是字符串的第 i 個(gè)字符试吁,n 是字符串的長(zhǎng)度肴茄,^ 表示求冪勉躺。(空字符串的哈希碼為 0瘫怜。)

3.這里我們首先要明白一個(gè)問(wèn)題:
equals()相等的兩個(gè)對(duì)象扇售,hashcode()一定相等鸳谜;
equals()不相等的兩個(gè)對(duì)象膝藕,卻并不能證明他們的hashcode()不相等。換句話說(shuō)咐扭,equals()方法不相等的兩個(gè)對(duì)象束莫,hashcode()有可能相等。(我的理解是由于哈希碼在生成的時(shí)候產(chǎn)生沖突造成的)草描。
反過(guò)來(lái):hashcode()不等览绿,一定能推出equals()也不等;hashcode()相等穗慕,equals()可能相等饿敲,也可能不等。解釋下第3點(diǎn)的使用范圍逛绵,我的理解是在object怀各、String等類中都能使用倔韭。在object類中,hashcode()方法是本地方法瓢对,返回的是對(duì)象的地址值寿酌,而object類中的equals()方法比較的也是兩個(gè)對(duì)象的地址值,如果equals()相等硕蛹,說(shuō)明兩個(gè)對(duì)象地址值也相等醇疼,當(dāng)然hashcode()也就相等了;在String類中法焰,equals()返回的是兩個(gè)對(duì)象內(nèi)容的比較秧荆,當(dāng)兩個(gè)對(duì)象內(nèi)容相等時(shí),
Hashcode()方法根據(jù)String類的重寫(第2點(diǎn)里面已經(jīng)分析了)代碼的分析埃仪,也可知道hashcode()返回結(jié)果也會(huì)相等乙濒。以此類推,可以知道Integer卵蛉、Double等封裝類中經(jīng)過(guò)重寫的equals()和hashcode()方法也同樣適合于這個(gè)原則颁股。當(dāng)然沒(méi)有經(jīng)過(guò)重寫的類,在繼承了object類的equals()和hashcode()方法后傻丝,也會(huì)遵守這個(gè)原則甘有。

4.談到hashcode()和equals()就不能不說(shuō)到hashset,hashmap,hashtable中的使用,具體是怎樣呢桑滩,請(qǐng)看如下分析:
Hashset是繼承Set接口梧疲,Set接口又實(shí)現(xiàn)Collection接口允睹,這是層次關(guān)系运准。那么hashset是根據(jù)什么原理來(lái)存取對(duì)象的呢?
在hashset中不允許出現(xiàn)重復(fù)對(duì)象缭受,元素的位置也是不確定的胁澳。在hashset中又是怎樣判定元素是否重復(fù)的呢?這就是問(wèn)題的關(guān)鍵所在米者,經(jīng)過(guò)一下午的查詢求證終于獲得了一點(diǎn)啟示韭畸,和大家分享一下,在java的集合中蔓搞,判斷兩個(gè)對(duì)象是否相等的規(guī)則是:
1)胰丁,判斷兩個(gè)對(duì)象的hashCode是否相等
如果不相等,認(rèn)為兩個(gè)對(duì)象也不相等喂分,完畢
如果相等锦庸,轉(zhuǎn)入2)
(這一點(diǎn)只是為了提高存儲(chǔ)效率而要求的,其實(shí)理論上沒(méi)有也可以蒲祈,但如果沒(méi)有甘萧,實(shí)際使用時(shí)效率會(huì)大大降低萝嘁,所以我們這里將其做為必需的。后面會(huì)重點(diǎn)講到這個(gè)問(wèn)題扬卷。)
2)牙言,判斷兩個(gè)對(duì)象用equals運(yùn)算是否相等
如果不相等,認(rèn)為兩個(gè)對(duì)象也不相等
如果相等怪得,認(rèn)為兩個(gè)對(duì)象相等(equals()是判斷兩個(gè)對(duì)象是否相等的關(guān)鍵)
為什么是兩條準(zhǔn)則咱枉,難道用第一條不行嗎?不行汇恤,因?yàn)榍懊嬉呀?jīng)說(shuō)了庞钢,hashcode()相等時(shí),equals()方法也可能不等因谎,所以必須用第2條準(zhǔn)則進(jìn)行限制基括,才能保證加入的為非重復(fù)元素。
比如下面的代碼:

public static void main(String args[]){ 
String s1=new String("zhaoxudong"); 
String s2=new String("zhaoxudong"); 
System.out.println(s1==s2);//false 
System.out.println(s1.equals(s2));//true 
System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode() 
System.out.println(s2.hashCode()); 
Set hashset=new HashSet(); 
hashset.add(s1); 
hashset.add(s2); 
/*實(shí)質(zhì)上在添加s1,s2時(shí)财岔,運(yùn)用上面說(shuō)到的兩點(diǎn)準(zhǔn)則风皿,可以知道
hashset認(rèn)為s1和s2是相等的,是在添加重復(fù)元素匠璧,所以讓s2覆蓋了s1;*/ 
Iterator it=hashset.iterator(); 
            while(it.hasNext()) 
            { 
             System.out.println(it.next()); 
            } 

最后在while循環(huán)的時(shí)候只打印出了一個(gè)”zhaoxudong”桐款。
輸出結(jié)果為:false
true
-967303459
-967303459
這是因?yàn)镾tring類已經(jīng)重寫了equals()方法和hashcode()方法,所以在根據(jù)上面的第1.2條原則判定時(shí)夷恍,hashset認(rèn)為它們是相等的對(duì)象魔眨,進(jìn)行了重復(fù)添加。
但是看下面的程序:

import java.util.*; 
public class HashSetTest 
{ 
   public static void main(String[] args) 
    { 
                 HashSet hs=new HashSet(); 
                 hs.add(new Student(1,"zhangsan")); 
                 hs.add(new Student(2,"lisi")); 
                 hs.add(new Student(3,"wangwu")); 
                 hs.add(new Student(1,"zhangsan")); 
  
                 Iterator it=hs.iterator(); 
                 while(it.hasNext()) 
                 { 
                        System.out.println(it.next()); 
                 } 
     } 
} 
class Student 
   { 
     int num; 
     String name; 
     Student(int num,String name) 
                { 
                this.num=num; 
                 this.name=name; 
                 } 
              public String toString() 
                { 
                    return num+":"+name; 
                 } 
           }      
輸出結(jié)果為: 
                      1:zhangsan 
                   1:zhangsan 
                   3:wangwu 
                   2:lisi 

問(wèn)題出現(xiàn)了酿雪,為什么hashset添加了相等的元素呢遏暴,這是不是和hashset的原則違背了呢?回答是:沒(méi)有
因?yàn)樵诟鶕?jù)hashcode()對(duì)兩次建立的new Student(1,"zhangsan")對(duì)象進(jìn)行比較時(shí)指黎,生成的是不同的哈希碼值朋凉,所以hashset把他當(dāng)作不同的對(duì)象對(duì)待了,當(dāng)然此時(shí)的equals()方法返回的值也不等(這個(gè)不用解釋了吧)醋安。那么為什么會(huì)生成不同的哈希碼值呢杂彭?上面我們?cè)诒容^s1和s2的時(shí)候不是生成了同樣的哈希碼嗎?原因就在于我們自己寫的Student類并沒(méi)有重新自己的hashcode()和equals()方法吓揪,所以在比較時(shí)亲怠,是繼承的object類中的hashcode()方法,呵呵柠辞,各位還記得object類中的hashcode()方法比較的是什么吧M呕唷!
它是一個(gè)本地方法,比較的是對(duì)象的地址(引用地址)徙垫,使用new方法創(chuàng)建對(duì)象讥裤,兩次生成的當(dāng)然是不同的對(duì)象了(這個(gè)大家都能理解吧。姻报。己英。),造成的結(jié)果就是兩個(gè)對(duì)象的hashcode()返回的值不一樣吴旋。所以根據(jù)第一個(gè)準(zhǔn)則损肛,hashset會(huì)把它們當(dāng)作不同的對(duì)象對(duì)待,自然也用不著第二個(gè)準(zhǔn)則進(jìn)行判定了荣瑟。那么怎么解決這個(gè)問(wèn)題呢治拿??
答案是:在Student類中重新hashcode()和equals()方法笆焰。

例如: 
  class Student 
{ 
int num; 
String name; 
Student(int num,String name) 
{ 
            this.num=num; 
            this.name=name; 
} 
public int hashCode() 
{ 
            return num*name.hashCode(); 
} 
public boolean equals(Object o) 
{ 
            Student s=(Student)o; 
            return num==s.num && name.equals(s.name); 
} 
public String toString() 
{ 
            return num+":"+name; 
} 
} 

根據(jù)重寫的方法劫谅,即便兩次調(diào)用了new Student(1,"zhangsan"),我們?cè)讷@得對(duì)象的哈希碼時(shí)嚷掠,根據(jù)重寫的方法hashcode()捏检,獲得的哈希碼肯定是一樣的(這一點(diǎn)應(yīng)該沒(méi)有疑問(wèn)吧)。
當(dāng)然根據(jù)equals()方法我們也可判斷是相同的不皆。所以在向hashset集合中添加時(shí)把它們當(dāng)作重復(fù)元素看待了贯城。所以運(yùn)行修改后的程序時(shí),我們會(huì)發(fā)現(xiàn)運(yùn)行結(jié)果是:
1:zhangsan
3:wangwu
2:lisi
可以看到重復(fù)元素的問(wèn)題已經(jīng)消除霹娄。
關(guān)于在hibernate的pojo類中能犯,重新equals()和hashcode()的問(wèn)題:
1),重點(diǎn)是equals犬耻,重寫hashCode只是技術(shù)要求(為了提高效率)
2)踩晶,為什么要重寫equals呢,因?yàn)樵趈ava的集合框架中香追,是通過(guò)equals來(lái)判斷兩個(gè)對(duì)象是否相等的
3)合瓢,在hibernate中坦胶,經(jīng)常使用set集合來(lái)保存相關(guān)對(duì)象透典,而set集合是不允許重復(fù)的。我們?cè)賮?lái)談?wù)勄懊嫣岬皆谙騢ashset集合中添加元素時(shí),怎樣判斷對(duì)象是否相同的準(zhǔn)則顿苇,前面說(shuō)了兩條峭咒,其實(shí)只要重寫equals()這一條也可以。
但當(dāng)hashset中元素比較多時(shí)纪岁,或者是重寫的equals()方法比較復(fù)雜時(shí)凑队,我們只用equals()方法進(jìn)行比較判斷,效率也會(huì)非常低,所以引入了hashcode()這個(gè)方法漩氨,只是為了提高效率西壮,但是我覺得這是非常有必要的(所以我們?cè)谇懊嬉詢蓷l準(zhǔn)則來(lái)進(jìn)行hashset的元素是否重復(fù)的判斷)。

比如可以這樣寫:
public int hashCode(){
return 1;}//等價(jià)于hashcode無(wú)效
這樣做的效果就是在比較哈希碼的時(shí)候不能進(jìn)行判斷叫惊,因?yàn)槊總€(gè)對(duì)象返回的哈希碼都是1款青,每次都必須要經(jīng)過(guò)比較equals()方法后才能進(jìn)行判斷是否重復(fù),這當(dāng)然會(huì)引起效率的大大降低霍狰。
我有一個(gè)問(wèn)題抡草,如果像前面提到的在hashset中判斷元素是否重復(fù)的必要方法是equals()方法(根據(jù)網(wǎng)上找到的觀點(diǎn)),但是這里并沒(méi)有涉及到關(guān)于哈希表的問(wèn)題蔗坯,可是這個(gè)集合卻叫hashset康震,這是為什么?宾濒?
我想腿短,在hashmap,hashtable中的存儲(chǔ)操作,依然遵守上面的準(zhǔn)則绘梦。所以這里不再多說(shuō)答姥。這些是今天看書,網(wǎng)上查詢資料谚咬,自己總結(jié)出來(lái)的鹦付,部分代碼和語(yǔ)言是引述,但是千真萬(wàn)確是自己總結(jié)出來(lái)的择卦。有錯(cuò)誤之處和不詳細(xì)不清楚的地方還請(qǐng)大家指出敲长,我也是初學(xué)者,所以難免會(huì)有錯(cuò)誤的地方秉继,希望大家共同討論祈噪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尚辑,隨后出現(xiàn)的幾起案子辑鲤,更是在濱河造成了極大的恐慌,老刑警劉巖杠茬,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件月褥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瓢喉,警方通過(guò)查閱死者的電腦和手機(jī)宁赤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)栓票,“玉大人决左,你說(shuō)我怎么就攤上這事。” “怎么了佛猛?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵惑芭,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我继找,道長(zhǎng)强衡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任码荔,我火速辦了婚禮漩勤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缩搅。我一直安慰自己越败,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布硼瓣。 她就那樣靜靜地躺著究飞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪堂鲤。 梳的紋絲不亂的頭發(fā)上亿傅,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音瘟栖,去河邊找鬼葵擎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛半哟,可吹牛的內(nèi)容都是我干的酬滤。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼寓涨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盯串!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起戒良,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤体捏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后糯崎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體几缭,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年拇颅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奏司。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乔询。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡樟插,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情黄锤,我是刑警寧澤搪缨,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站鸵熟,受9級(jí)特大地震影響副编,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜流强,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一痹届、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧打月,春花似錦队腐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至秘通,卻和暖如春为严,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肺稀。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工第股, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人话原。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓炸茧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親稿静。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梭冠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法改备,內(nèi)部類的語(yǔ)法控漠,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法悬钳,線程的語(yǔ)...
    子非魚_t_閱讀 31,669評(píng)論 18 399
  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等盐捷,對(duì)于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,504評(píng)論 0 3
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司默勾,掛了不少碉渡,但最終還是拿到小米、百度母剥、阿里滞诺、京東形导、新浪、CVTE习霹、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,281評(píng)論 11 349
  • 13.只是致良知三字無(wú)病 王陽(yáng)明早期曾以靜坐教人收斂心神朵耕,但靜坐久了,有的學(xué)子會(huì)流入談玄說(shuō)虛的神秘經(jīng)驗(yàn)中淋叶,因此陽(yáng)明...
    真zhen閱讀 920評(píng)論 0 2
  • 這幾天忙得焦頭爛額阎曹。 前一段時(shí)間空閑得很,并且又馬上面臨暑期長(zhǎng)假煞檩。本以為可以連續(xù)30天高質(zhì)量地完成每天500+字的...
    笙兒姐閱讀 256評(píng)論 0 0