《Effective Java》學(xué)習(xí)筆記之改寫equals和hashCode方法

何時(shí)改寫equals方法

當(dāng)一個(gè)類有自己特有的"邏輯相等"概念,而且超類也沒有實(shí)現(xiàn)equals方法實(shí)現(xiàn)期望的行為谬哀,這是我們需要改寫equals方法悦屏。這種比較適合"值類"情形,比如Integer或者Date卖怜。當(dāng)需要自定義類最為HashMap的鍵時(shí)屎开,也需要改寫equals方法,與此同時(shí)還需改寫hashcode()方法。

對(duì)于改寫equals方法所需遵守的通過規(guī)定:

  • 自反性:對(duì)于任何非空引用值 x奄抽,x.equals(x) 都應(yīng)返回 true蔼两。

  • 對(duì)稱性:對(duì)于任何非空引用值 x 和 y,當(dāng)且僅當(dāng) y.equals(x) 返回 true 時(shí)逞度,x.equals(y) 才應(yīng)返回 true额划。

  • 傳遞性:對(duì)于任何非空引用值 x、y 和 z档泽,如果 x.equals(y) 返回 true俊戳,并且 y.equals(z) 返回 true,那么 x.equals(z) 應(yīng)返回 true馆匿。

  • 一致性:對(duì)于任何非空引用值 x 和 y抑胎,多次調(diào)用 x.equals(y) 始終返回 true 或始終返回 false,前提是對(duì)象上 equals 比較中所用的信息沒有被修改渐北。

  • 對(duì)于任何非空引用值 x阿逃,x.equals(null) 都應(yīng)返回 false。

對(duì)于上面幾個(gè)規(guī)則赃蛛,我們在使用的過程中最好遵守恃锉,否則會(huì)出現(xiàn)意想不到的錯(cuò)誤。
這里說明一點(diǎn):對(duì)于改寫equals中的"非空性"呕臂,為了測試實(shí)參與當(dāng)前對(duì)象的相等情況破托,equals方法首先把實(shí)參轉(zhuǎn)為一種適當(dāng)?shù)念愋停谵D(zhuǎn)換之前歧蒋,equals方法必須使用instanceof操作符炼团,檢查實(shí)參是否為正確的類型:

 public boolean equals(Object o) {
        if (!(o instanceof Mytype)) {
            return false;
            ....
        }
    }

如果o為null,則類型檢查結(jié)果為false,所以不需做單獨(dú)的null檢查疏尿。


實(shí)現(xiàn)高質(zhì)量的equals方法:

  • 使用==操作符檢查"實(shí)參是否為指向?qū)ο蟮囊粋€(gè)引用".
  • 使用instanceof操作符檢查"實(shí)參是否為正確的類型"
  • 把實(shí)參轉(zhuǎn)換為正確的類型
  • 對(duì)于每一個(gè)"關(guān)鍵域",檢查實(shí)參中的域與當(dāng)前對(duì)象中對(duì)應(yīng)的域值是否匹配
    比較域值的常用方法(防止原對(duì)象引用域值本身為Null的情況)
(field == null ? o.field == null :field.equals(o.field) );

如果是對(duì)象引用瘟芝,那么下面的方法會(huì)更快一點(diǎn):

(field == o.field || (o.field != null && field.equals(o.field) );

  • 改寫equals方法之后,改寫hashcode
  • 不要將equals聲明中的Object對(duì)象換成其他類型褥琐。
    以下代碼是錯(cuò)誤的例子:
public boolean equals(Mytype o) {
        ...
}

以下是一個(gè)樣例:

public class Mytype
 {
    private String field;
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (!(o instanceof Mytype)) {
            return false;
        }
        Mytype my = (Mytype)o;
        return (field == my.field ||(my.field != null &&field.equals(my.field)));
    }
    public boolean equals(Mytype o) {

    }
}

有時(shí)候?qū)崊⑴c對(duì)象本身比較也省略掉了锌俱。


改寫hashCode方法

在每個(gè)改寫equals方法的類中,你也必須改寫hashCode()方法敌呈。

hashcode的約定:

  • 如果一個(gè)對(duì)象的equals方法作比較所用的信息沒有被修改的話贸宏,那么,對(duì)該對(duì)象調(diào)用hashcode方法多次磕洪,始終返回用一個(gè)整數(shù)值吭练。
  • 如果兩個(gè)對(duì)象根據(jù)equals方法是相等的,那么它們具有相同的散列碼
  • 如果兩個(gè)對(duì)象根據(jù)equals方法不是相等的析显,那么調(diào)用這兩個(gè)對(duì)象中任一個(gè)對(duì)象的hashcode方法鲫咽,不要求必須產(chǎn)生不同的整數(shù)結(jié)果。

未重寫hashcode方法引發(fā)的問題

如果一個(gè)類A只重寫了equals方法,當(dāng)它的實(shí)例對(duì)象被用作HashMap的鍵時(shí)分尸,在獲取這個(gè)鍵對(duì)應(yīng)的值的時(shí)候锦聊,問題來了,用put(K1,V)方法和get(K1)方法時(shí)箩绍,這里涉及類A的兩個(gè)實(shí)例孔庭,第二個(gè)對(duì)象和第一個(gè)實(shí)例相等,第二個(gè)實(shí)例用于檢索時(shí)材蛛,問題發(fā)生了圆到,類A并沒有重寫hashcode方法,所以兩個(gè)實(shí)例具有不同的散列碼卑吭,違反了hashcode的約定芽淡,因此,Put方法把對(duì)象K1放在一個(gè)散列桶里陨簇,而get方法去另一個(gè)散列桶里查找他的對(duì)象。

改寫hashcode的"部分良方"

以下是從《Effective Java》截取的一部分良方迹淌,如果想看更詳細(xì)的方案河绽,可以參閱《Effective Java》這本書。

  • 把數(shù)值17(或者其他數(shù)值)保存在一個(gè)result的int類型的變量中
  • 計(jì)算int類型的散列碼
  • boolean類型計(jì)算:(f ? 0:1)
  • 以下是例子:
@Override  
    public int hashCode() {  
        int result = 17;  
        result = result * 37 + string.hashCode();  
        result = result * 37 + number;  
        return result;  
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唉窃,一起剝皮案震驚了整個(gè)濱河市耙饰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纹份,老刑警劉巖苟跪,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蔓涧,居然都是意外死亡件已,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門元暴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篷扩,“玉大人,你說我怎么就攤上這事茉盏〖矗” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鸠姨,是天一觀的道長铜秆。 經(jīng)常有香客問我,道長讶迁,這世上最難降的妖魔是什么连茧? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上梅屉,老公的妹妹穿的比我還像新娘值纱。我一直安慰自己,他們只是感情好坯汤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布虐唠。 她就那樣靜靜地躺著,像睡著了一般惰聂。 火紅的嫁衣襯著肌膚如雪疆偿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天搓幌,我揣著相機(jī)與錄音杆故,去河邊找鬼。 笑死溉愁,一個(gè)胖子當(dāng)著我的面吹牛处铛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拐揭,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼撤蟆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了堂污?” 一聲冷哼從身側(cè)響起家肯,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盟猖,沒想到半個(gè)月后讨衣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡式镐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年反镇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娘汞。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡愿险,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出价说,到底是詐尸還是另有隱情辆亏,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布鳖目,位于F島的核電站扮叨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏领迈。R本人自食惡果不足惜彻磁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一碍沐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衷蜓,春花似錦累提、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至置吓,卻和暖如春无虚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衍锚。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工友题, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人戴质。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓度宦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親告匠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子戈抄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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