談?wù)凧ava中hashCode和equals方法

beverage-blue-sky.jpg

在Java學(xué)習(xí)中霍狰,hashCode和equals方法灿巧,是一個繞不開的話題浮定。
這兩個方法到底有什么作用扇苞?這兩個方法什么時候需要重寫?如何重寫橡娄?
這篇文章诗箍,我們一起捋一捋Java中的hashCode和equals方法。

hashCode和equals是Object的方法

Object類方法.png

我們可以參考查閱JDK8的官方API文檔:https://docs.oracle.com/javase/8/docs/api/index.html

Object類的描述

Class Object是類層次結(jié)構(gòu)的根挽唉。每個類都有Object作為超類扳还。所有對象(包括數(shù)組)都實(shí)現(xiàn)此類的方法。
所有的Java類默認(rèn)繼承Object類橱夭。只是隱式地默認(rèn)集成。而沒有使用extends關(guān)鍵字桑逝。

hashCode方法描述

返回對象的哈希碼值棘劣。
提供此方法是因?yàn)楣1淼膬?yōu)勢,例如HashMap中使用的哈希表楞遏。
hashCode的一般契約是:

  • 每當(dāng)在執(zhí)行Java應(yīng)用程序期間多次在同一對象上調(diào)用它時茬暇,hashCode方法必須始終返回相同的整數(shù),前提是不修改對象的equals比較中使用的信息寡喝。從應(yīng)用程序的一次執(zhí)行到同一應(yīng)用程序的另一次執(zhí)行糙俗,該整數(shù)不需要保持一致。

解讀:
一次執(zhí)行中的多次調(diào)用预鬓,同一個對象需要返回同一個hashcode值巧骚。
多次執(zhí)行同一個對象,每次返回的hashcode值可以不一樣格二。

  • 如果兩個對象根據(jù)equals(Object)方法相等劈彪,則對兩個對象中的每一個調(diào)用hashCode方法必須生成相同的整數(shù)結(jié)果。
  • 如果兩個對象根據(jù)equals(java.lang.Object)方法不相等顶猜,則不需要在兩個對象中的每一個上調(diào)用hashCode方法必須生成不同的整數(shù)結(jié)果沧奴。但是,程序員應(yīng)該知道长窄,為不等的對象生成不同的整數(shù)結(jié)果可能會提高散列表的性能滔吠。

盡可能合理,Object類定義的hashCode方法確實(shí)為不同的對象返回不同的整數(shù)挠日。(這通常通過將對象的內(nèi)部地址轉(zhuǎn)換為整數(shù)來實(shí)現(xiàn)疮绷,但Java?編程語言不需要此實(shí)現(xiàn)技術(shù)。)

equals方法描述

表明某個其他對象是否“等于”此對象肆资。
equals方法在非null對象引用上實(shí)現(xiàn)等價關(guān)系:

  • 自反性:對于任何非空引用值x矗愧,x.equals(x)應(yīng)該返回true。
  • 對稱性:對于任何非空引用值x和y,當(dāng)且僅當(dāng)y.equals(x)返回true時唉韭,x.equals(y)才應(yīng)返回true夜涕。
  • 傳遞性:對于任何非空引用值x,y和z属愤,如果x.equals(y)返回true而y.equals(z)返回true女器,則x.equals(z)應(yīng)返回true。
  • 一致性:對于任何非空引用值x和y住诸,x.equals(y)的多次調(diào)用始終返回true或始終返回false驾胆,前提是不修改在對象的equals比較中使用的信息。對于任何非空引用值x贱呐,x.equals(null)應(yīng)返回false丧诺。

類Object的equals方法實(shí)現(xiàn)了對象上最具辨別力的等價關(guān)系;也就是說,對于任何非空引用值x和y奄薇,當(dāng)且僅當(dāng)x和y引用同一對象時驳阎,此方法才返回true(x == y的值為true)。

請注意馁蒂,通常需要在重寫此方法時覆蓋hashCode方法呵晚,以便維護(hù)hashCode方法的常規(guī)協(xié)定,該方法聲明相等的對象必須具有相等的哈希代碼沫屡。

針對上述官方描述饵隙,我們繼續(xù)查看equals方法:

public boolean equals(Object obj) {
        return (this == obj);
    }

我們追求的是業(yè)務(wù)上的內(nèi)容相等

查看上述源碼可知,在Object類中沮脖,equals比較的是對象地址是否相等金矛,即是否是同一個對象,而不是比較值是否相等勺届。

而我們業(yè)務(wù)中通常追求的是只是業(yè)務(wù)上的內(nèi)容相等绷柒,所以我們業(yè)務(wù)中使用的類都是需要重寫equals和hashCode方法的。

重寫equals方法涮因,同時必須重寫hashCode方法

我們從Objec類官方API文檔中废睦,可知:
“如果兩個對象根據(jù)equals(Object)方法相等,則對兩個對象中的每一個調(diào)用hashCode方法必須生成相同的整數(shù)結(jié)果”养泡。
所以我們重寫equals方法的時候嗜湃,為了滿足這個原則,也必須同時重寫hashCode方法澜掩。

public class ObjectEquals {
    public static void main(String[] args) {
        Student student1 = new Student("LeBron Jame", 12);
        Student student2 = new Student("LeBron Jame", 12);
        System.out.println(student1.equals(student2));
    }

    private static class Student {
        String name;
        Integer age;

        public Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    }
}

上述示例中购披,返回結(jié)果為false,因?yàn)镾tudent類并沒有重寫equals方法肩榕,而是直接使用Object類中的equals方法刚陡,直接用==比較兩個對象指向的地址是否一致惩妇,即是否是同一個對象,上述兩個對象只是內(nèi)容相同筐乳,但不是同一個對象歌殃,所以結(jié)果返回false。

public class ObjectEquals {
    public static void main(String[] args) {
        Student student1 = new Student("LeBron Jame", 12);
        Student student2 = new Student("LeBron Jame", 12);
        System.out.println(student1.equals(student2));
    }

    private static class Student {
        String name;
        Integer age;

        public Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (this == obj) {
                return true;
            }
            Student student = (Student) obj;
            return (name.equals(student.name) && age.equals(student.age));
        }

    }
}

上述示例蝙云,重寫Objec的equlas方法之后氓皱,只比較兩個對象的內(nèi)容是否相同,所以結(jié)果返回true勃刨。這就是業(yè)務(wù)上的內(nèi)容相等波材。

System.out.println("對象student1的哈希值:"+student1.hashCode());
System.out.println("對象student2的哈希值:"+student2.hashCode());

上述示例中,我們增加打印兩個對象的hashCode值身隐,運(yùn)行結(jié)果為:

對象student1的哈希值:1735600054
對象student2的哈希值:21685669

所以廷区,如果我們只重寫equals方法,但是不重寫hashCode方法的話贾铝,兩個對象的hashCode不一致躲因。

@Override
        public int hashCode() {
            return name.hashCode() * 31 + age;
        }

上述示例,我們重寫hashCode方法忌傻,運(yùn)行結(jié)果:

對象student1的哈希值:147570189
對象student2的哈希值:147570189

可知,兩個對象的hashCode值一致了搞监。

總結(jié)

  • 重寫equals方法的時候水孩,必須同步重寫hashCode方法。
  • 兩個對象equals相等琐驴,hashCode值必然相等俘种。
  • 兩個對象hashCode值相等,equals不一定相等绝淡,因?yàn)榭赡艽嬖诠_突宙刘。
  • hashCode方法,主要用于Java中的集合類牢酵,通過hashCode值進(jìn)行高效率查找悬包。

PS

  • 強(qiáng)烈推薦學(xué)習(xí)Java多看官方API文檔,結(jié)合官方API文檔看源碼馍乙,追本溯源布近。
  • 強(qiáng)烈推薦閱讀《Effective Java》,非常經(jīng)典的一本好書丝格。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撑瞧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子显蝌,更是在濱河造成了極大的恐慌预伺,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異酬诀,居然都是意外死亡脏嚷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門料滥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來然眼,“玉大人,你說我怎么就攤上這事葵腹「呙浚” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵践宴,是天一觀的道長鲸匿。 經(jīng)常有香客問我,道長阻肩,這世上最難降的妖魔是什么带欢? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮烤惊,結(jié)果婚禮上乔煞,老公的妹妹穿的比我還像新娘。我一直安慰自己柒室,他們只是感情好渡贾,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雄右,像睡著了一般空骚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上擂仍,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天囤屹,我揣著相機(jī)與錄音,去河邊找鬼逢渔。 笑死肋坚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肃廓。 我是一名探鬼主播冲簿,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼亿昏!你這毒婦竟也來了峦剔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤角钩,失蹤者是張志新(化名)和其女友劉穎吝沫,沒想到半個月后呻澜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惨险,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年羹幸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辫愉。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡栅受,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恭朗,到底是詐尸還是另有隱情屏镊,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布痰腮,位于F島的核電站而芥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏膀值。R本人自食惡果不足惜棍丐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沧踏。 院中可真熱鬧歌逢,春花似錦、人聲如沸翘狱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盒蟆。三九已至,卻和暖如春师骗,著一層夾襖步出監(jiān)牢的瞬間历等,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工辟癌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寒屯,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓黍少,卻偏偏與公主長得像寡夹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子厂置,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359