equals 與 hashCode 筆記二

第二篇文章主要講解 hashCode 方法篮撑,分為以下部分:

  1. 關(guān)于 hashCode
  2. hashCode 源碼
  3. 重寫 hashCode 原因
  4. 如何重寫 hashCode

關(guān)于 hashCode

Java中的集合(Collection)有兩類舌狗,一類是 List,再有一類是 Set辜羊。前者集合內(nèi)的元素是有序的,元素可以重復(fù);后者元素?zé)o序拍棕,但元素不可重復(fù)解总。

兩個元素是否重復(fù)應(yīng)該依據(jù)什么來判斷呢贮匕?

答案是 equals 方法。如果每增加一個元素就檢查一次花枫,在集合類的元素較少時刻盐,執(zhí)行效率還算不錯,但是那么當(dāng)元素很多時劳翰,后添加到集合中的元素比較的次數(shù)就非常多了敦锌。也就是說,如果集合中現(xiàn)在已經(jīng)有 1000 個元素佳簸,那么第 1001 個元素加入集合時乙墙,它就要調(diào)用 1000 次 equals 方法。這顯然會大大降低效率。

一般情況下伶丐,計算機存儲數(shù)據(jù)時悼做,將數(shù)據(jù)連續(xù)地存儲在內(nèi)存單元中,于是哗魂,Java集合類在存儲數(shù)據(jù)時肛走,引進了hashCode方法,它采用了一種稱為哈希算法录别,即將數(shù)據(jù)通過特定算法指定到某個內(nèi)存單元朽色。

當(dāng)集合要添加新的元素時,先調(diào)用這個元素的hashCode方法组题,就一下子能定位到它應(yīng)該放置的物理位置上葫男。如果這個位置上沒有元素,它就可以直接存儲在這個位置上崔列,不用再進行任何比較了梢褐;如果這個位置上已經(jīng)有元素了,就調(diào)用它的 equals 方法與新元素進行比較赵讯,相同的話就不存了盈咳,不相同就散列其它的地址。所以這里存在一個沖突解決的問題边翼。這樣一來實際調(diào)用 equals 方法的次數(shù)就大大降低了鱼响,幾乎只需要一兩次。

所以组底,Java 對于 eqauls 方法和 hashCode 方法是這樣規(guī)定的:

1.如果兩個對象相同丈积,那么它們的 hashCode 值一定要相同;
2.如果兩個對象的 hashCode 相同债鸡,它們并不一定相同(這里說的對象相同指的是用 eqauls 方法比較)江滨。

  1. equals() 相等的兩個對象,hashCode() 一定相等娘锁;equals() 不相等的兩個對象牙寞,卻并不能證明他們的 hashCode() 不相等。

注: 規(guī)定 2 中指的是兩個對象在哈希存儲時發(fā)生了沖突莫秆。

hashCode 源碼

public native int hashCode();

hashCode() 是一個本地方法间雀,返回這個對象的哈希值,默認是返回該對象的內(nèi)存地址镊屎。重寫此方法可以提高哈希結(jié)構(gòu)的集合的性能惹挟。

重寫 hashCode 原因

對于 Java 集合類,我們經(jīng)常使用 Set 集合來保存相關(guān)對象缝驳,而 Set 集合是不允許重復(fù)的连锯。在向 HashSet 集合中添加元素時归苍,其實只要重寫 equals() 這一條也可以。但當(dāng) HashSet 中元素比較多時运怖,或者是重寫的 equals() 方法比較復(fù)雜時拼弃,我們只用 equals() 方法進行比較判斷,效率也會非常低摇展,所以引入了 hashCode() 這個方法吻氧,只是為了提高效率,且這是非常有必要的咏连。

簡單來說盯孙,hashCode存在的意義主要是提供查找的快捷性,比如說在Hashtable祟滴、HashMap中等振惰。hashCode是用來在散列存儲結(jié)構(gòu)中確定對象存儲的位置的;

如何重寫 hashCode

重寫 hashCode 所要遵循的原則如下:

  • 在程序執(zhí)行期間,只要 equals 方法的比較操作用到的信息沒有被修改垄懂,那么對這同一個對象調(diào)用多次骑晶,hashCode 方法必須始終如一地返回同一個整數(shù)
  • 如果兩個對象通過 equals 方法比較得到的結(jié)果是相等的,那么對這兩個對象進行hashCode得到的值應(yīng)該相同
  • 兩個不同的對象草慧,hashCode 的結(jié)果可能是相同的透罢,這就是哈希表中的沖突。為了保證哈希表的效率冠蒋,哈希算法應(yīng)盡可能的避免沖突

下面介紹如何來重寫hashCode()方法。通常重寫hashCode()方法按以下設(shè)計原則實現(xiàn)乾胶。

  1. 把某個非零素數(shù)抖剿,例如17,保存在int型變量result中识窿。
  2. 對于對象中每一個關(guān)鍵域f(指equals方法中考慮的每一個域)參照以下原則處理斩郎。
  • boolean型,計算(f?0:1)喻频。
  • byte缩宜、char和short型,計算(int)f甥温。
  • long型锻煌,計算(int)(f^(f>>32))。
  • float型姻蚓,計算Float.floatToIntBits(f)。
  • double型,計算Double.doubleToLongBits(f)得到一個long瘪板,再執(zhí)行l(wèi)ong型的處理淑仆。
  • 對象引用释涛,遞歸調(diào)用它的hashCode()方法。
  • 數(shù)組域倦沧,對其中的每個元素調(diào)用它的hashCode()方法唇撬。
  1. 將上面計算得到的散列碼保存到int型變量c,然后執(zhí)行result = 37 * result + c展融,并返回窖认。

根據(jù)上面的理解,我們進行相關(guān)測試愈污。
Person類的 hashCode 重寫如下:

@Override
 public int hashCode() {
    
    int result = 17;
    result = 37 * result + name.hashCode();
    return result;
}

Employee類的 hashCode 重寫如下:

@Override
public int hashCode() {

   int result = 17;
   result = 37 * result + super.hashCode();
   result = 37 * result + id;
   return result;
}

測試類 HashCodeTest 源碼如下:

public class HashCodeTest {

    public static void main(String[] args) {
        
        Employee e1 = new Employee("Mary", 18);
        Employee e2 = new Employee("Mary", 19);
        Person p1 = new Person("Mary");
        Person p2 = new Person("Mary");

        System.out.println("p1.equals(e1)'s rsult:" + p1.equals(e1));
        System.out.println("p1.equals(e2)'s rsult:" + p1.equals(e2));
        System.out.println("e1.equals(e2)'s rsult:" + e1.equals(e2));
        System.out.println("p1.equals(p2)'s rsult:" + p1.equals(p2));
        
        System.out.println("p1.hashCode is :" + p1.hashCode());
        System.out.println("p2.hashCode is :" + p2.hashCode());
        System.out.println("e1.hashCode is :" + e1.hashCode());
        System.out.println("e2.hashCode is :" + e2.hashCode());
    }

}

測試結(jié)果如下:

p1.equals(e1)'s rsult:false
p1.equals(e2)'s rsult:false
e1.equals(e2)'s rsult:false
p1.equals(p2)'s rsult:true
p1.hashCode is :2391408
p2.hashCode is :2391408
e1.hashCode is :88505387
e2.hashCode is :88505388

即 equals() 相等的兩個對象耀态,hashcode() 一定相等。創(chuàng)建一個類時暂雹,我們需要重寫該類的 equals 和 hashCode 方法首装,若不對其進行重寫,則會默認為 Object 類的 equals 和 hashCode 方法杭跪。

參考鏈接

  1. Java 中 equals() 與 hashCode() 方法詳解
  2. hashCode 方法及 equals 方法的規(guī)范
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仙逻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子涧尿,更是在濱河造成了極大的恐慌系奉,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姑廉,死亡現(xiàn)場離奇詭異缺亮,居然都是意外死亡,警方通過查閱死者的電腦和手機桥言,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門萌踱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人号阿,你說我怎么就攤上這事并鸵。” “怎么了扔涧?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵园担,是天一觀的道長。 經(jīng)常有香客問我枯夜,道長弯汰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任湖雹,我火速辦了婚禮蝙泼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘劝枣。我一直安慰自己汤踏,他們只是感情好织鲸,可當(dāng)我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著溪胶,像睡著了一般搂擦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哗脖,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天瀑踢,我揣著相機與錄音,去河邊找鬼才避。 笑死橱夭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的桑逝。 我是一名探鬼主播棘劣,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼楞遏!你這毒婦竟也來了茬暇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤寡喝,失蹤者是張志新(化名)和其女友劉穎糙俗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體预鬓,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡巧骚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了格二。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片网缝。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蟋定,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情草添,我是刑警寧澤驶兜,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站远寸,受9級特大地震影響抄淑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驰后,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一肆资、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧灶芝,春花似錦郑原、人聲如沸唉韭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽属愤。三九已至,卻和暖如春酸役,著一層夾襖步出監(jiān)牢的瞬間住诸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工涣澡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贱呐,地道東北人。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓入桂,卻偏偏與公主長得像奄薇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子事格,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,937評論 2 361

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法惕艳,類相關(guān)的語法,內(nèi)部類的語法驹愚,繼承相關(guān)的語法远搪,異常的語法,線程的語...
    子非魚_t_閱讀 31,669評論 18 399
  • 本文出自 Eddy Wiki 逢捺,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 1,166評論 0 16
  • 首先equals()和hashcode()這兩個方法都是從object類中繼承過來的谁鳍。 很明顯是對兩個對象的地址值...
    itachi閱讀 313評論 0 3
  • Java8張圖 11、字符串不變性 12劫瞳、equals()方法倘潜、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,710評論 0 11
  • 2007-11-20 20:14 其實志于,我一直都記得你涮因,記得你風(fēng)中微笑的模樣那樣坦率那么純凈記憶里總有泛黃的角落悄...
    Helloe閱讀 196評論 0 1