面試官:“你重寫過 hashcode 和 equals 么缀皱,為什么重寫equals時必須重寫hashCode方法斗这?”

1.hashCode介紹

hashCode() 的作用是獲取哈希碼,也稱為散列碼啤斗;它實際上是返回一個int整數(shù)表箭。這個散列碼的作用是確定該對象在散列表中的索引位置,如果有看我的上一篇文章 什么是散列表争占,那么這里的散列碼就相當(dāng)于上文中根據(jù)首字母查詢散列表例子中 人名關(guān)鍵字k在散列表中的具體地址燃逻。hashCode() 定義在JDK的Object.java中序目,這就意味著Java中的任何類都包含有hashCode() 函數(shù)臂痕。

2.hashCode 的作用

數(shù)組是java中效率最高的數(shù)據(jù)結(jié)構(gòu),但是“最高”是有前提的猿涨。第一我們需要知道所查詢數(shù)據(jù)的所在位置握童。第二:如果我們進(jìn)行迭代查找時,數(shù)據(jù)量一定要小叛赚,對于大數(shù)據(jù)量而言一般推薦集合澡绩。

在 Java 集合中有兩類,一類是 List俺附,一類是 Set 他們之間的區(qū)別就在于 List 集合中的元素師有序的肥卡,且可以重復(fù),而 Set 集合中元素是無序不可重復(fù)的事镣。對于 List 好處理步鉴,但是對于 Set 而言我們要如何來保證元素不重復(fù)呢?通過迭代來 equals() 是否相等璃哟。數(shù)據(jù)量小還可以接受氛琢,當(dāng)我們的數(shù)據(jù)量大的時候效率可想而知(當(dāng)然我們可以利用算法進(jìn)行優(yōu)化)。比如我們向 HashSet 插入 1000 數(shù)據(jù)随闪,難道我們真的要迭代 1000 次阳似,調(diào)用 1000 次 equals() 方法嗎?hashCode 提供了解決方案铐伴。怎么實現(xiàn)撮奏?我們先看 hashCode 的源碼(Object)俏讹。

    public native int hashCode();

它是一個本地方法,它的實現(xiàn)與本地機(jī)器有關(guān)畜吊。當(dāng)我們向一個集合中添加某個元素藐石,集合會首先調(diào)用 hashCode 方法,這樣就可以直接定位它所存儲的位置定拟,若該處沒有其他元素于微,則直接保存。若該處已經(jīng)有元素存在青自,就調(diào)用 equals 方法來匹配這兩個元素是否相同株依,相同則不存,不同則散列到其他位置延窜。這樣處理恋腕,當(dāng)我們存入大量元素時就可以大大減少調(diào)用 equals() 方法的次數(shù),極大地提高了效率逆瑞。

所以 hashCode 在上面扮演的角色為尋域(尋找某個對象在集合中區(qū)域位置)荠藤。hashCode 可以將集合分成若干個區(qū)域,每個對象都可以計算出他們的 散列碼获高,可以將 散列碼分組哈肖,每個分組對應(yīng)著某個存儲區(qū)域(散列表),根據(jù)一個對象的 散列碼就可以確定該對象所存儲區(qū)域念秧,這樣就大大減少查詢匹配元素的數(shù)量淤井,提高了查詢效率。

3.hashCode 對于一個對象的重要性

hashCode 重要么摊趾?不重要币狠,對于 List 集合、數(shù)組而言砾层,但是對于 HashMap漩绵、HashSet、HashTable 而言肛炮,它變得異常重要止吐。所以在使用 HashMap、HashSet铸董、HashTable 時一定要注意 hashCode祟印。對于一個對象而言,其 hashCode 過程就是一個簡單的 Hash 算法的實現(xiàn)粟害,其實現(xiàn)過程對你實現(xiàn)對象的存取過程起到非常重要的作用蕴忆。

以 HashTable 為例闡述 hashCode 對于一個對象的重要性。

一個對象勢必會存在若干個屬性悲幅,如何選擇屬性來進(jìn)行散列考驗著一個人的設(shè)計能力套鹅。如果我們將所有屬性進(jìn)行散列站蝠,這必定會是一個糟糕的設(shè)計,因為對象的 hashCode 方法無時無刻不是在被調(diào)用卓鹿,如果太多的屬性參與散列菱魔,那么需要的操作數(shù)時間將會大大增加,這將嚴(yán)重影響程序的性能吟孙。但是如果較少屬相參與散列澜倦,散列的多樣性會削弱,會產(chǎn)生大量的散列“沖突”杰妓,除了不能夠很好的利用空間外藻治,在某種程度也會影響對象的查詢效率。其實這兩者是一個矛盾體巷挥,散列的多樣性會帶來性能的降低桩卵。

那么如何對對象的 hashCode 進(jìn)行設(shè)計,本人 也沒有經(jīng)驗倍宾。從網(wǎng)上查到了這樣一種解決方案:設(shè)置一個緩存標(biāo)識來緩存當(dāng)前的散列碼雏节,只有當(dāng)參與散列的對象改變時才會重新計算,否則調(diào)用緩存的 hashCode高职,這樣就可以從很大程度上提高性能钩乍。

在 HashTable 計算某個對象在 table[] 數(shù)組中的索引位置,其代碼如下:

    int index = (hash & 0x7FFFFFFF) % tab.length;

為什么要 &0x7FFFFFFF初厚?因為某些對象的 hashCode 可能會為負(fù)值件蚕,與 0x7FFFFFFF 進(jìn)行與運算可以確保 index 為一個正數(shù)。通過這步我可以直接定位某個對象的位置产禾,所以從理論上來說我們是完全可以利用 hashCode 直接定位對象的散列表中的位置,但是為什么會存在一個 key-value 的鍵值對牵啦,利用 key 的 hashCode 來存入數(shù)據(jù)而不是直接存放 value 呢亚情?這就關(guān)系 HashTable 性能問題的最重要的問題: Hash 沖突!(詳見上篇)

我們知道沖突的產(chǎn)生是由于不同的對象產(chǎn)生了相同的散列碼哈雏, hashcode 返回的是 int楞件,它的值只可能在 int 范圍內(nèi)。如果我們存放的數(shù)據(jù)超過了 int 的范圍呢裳瘪?這樣就必定會產(chǎn)生兩個相同的 散列碼土浸,這時在 散列碼 位置處會存儲兩個對象,我們就可以利用 key 本身來進(jìn)行判斷彭羹。所以具有相索引的對象黄伊,在該 散列碼 位置處存在多個對象,我們必須依靠 key 的 hashCode 和key 本身來進(jìn)行區(qū)分派殷。而Key的比較就用到了equals

4.hashCode 與 equals

簡而言之就是:

  • 如果兩個對象相等还最,則hashcode一定也是相同的
  • 兩個對象相等,對兩個對象分別調(diào)用equals方法都返回true
  • 兩個對象有相同的hashcode值墓阀,它們也不一定是相等的
  • 因此,equals 方法被覆蓋過拓轻,則 hashCode 方法也必須被覆蓋
  • hashCode() 的默認(rèn)行為是對堆上的對象產(chǎn)生獨特值斯撮。如果沒有重寫 hashCode(),則該 class 的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數(shù)據(jù))

對象的比較過程如下:

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扶叉,一起剝皮案震驚了整個濱河市勿锅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枣氧,老刑警劉巖粱甫,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異作瞄,居然都是意外死亡茶宵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門宗挥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乌庶,“玉大人,你說我怎么就攤上這事契耿÷鞔螅” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵搪桂,是天一觀的道長透敌。 經(jīng)常有香客問我,道長踢械,這世上最難降的妖魔是什么酗电? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮内列,結(jié)果婚禮上撵术,老公的妹妹穿的比我還像新娘。我一直安慰自己话瞧,他們只是感情好嫩与,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著交排,像睡著了一般划滋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上埃篓,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天处坪,我揣著相機(jī)與錄音,去河邊找鬼。 笑死稻薇,一個胖子當(dāng)著我的面吹牛嫂冻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播塞椎,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼桨仿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了案狠?” 一聲冷哼從身側(cè)響起服傍,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎骂铁,沒想到半個月后吹零,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡拉庵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年灿椅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钞支。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡茫蛹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出烁挟,到底是詐尸還是另有隱情婴洼,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布撼嗓,位于F島的核電站柬采,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏且警。R本人自食惡果不足惜粉捻,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望振湾。 院中可真熱鬧杀迹,春花似錦、人聲如沸押搪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽大州。三九已至,卻和暖如春垂谢,著一層夾襖步出監(jiān)牢的瞬間厦画,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留根暑,地道東北人力试。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像排嫌,于是被迫代替她去往敵國和親畸裳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355