Java開發(fā)HashMap?key必須實(shí)現(xiàn)hashCode?equals方法原理
一、問題引入
平時(shí)在開發(fā)中,相信你多多少少都使用過HashMap
,而當(dāng)你用自定義對象作為key
時(shí),很多人會(huì)告訴你:你必須要同時(shí)實(shí)現(xiàn)自定義對象的hashCode、equals
方法,否者可能會(huì)出問題,于是你就實(shí)現(xiàn)了...
可是為什么呢?比如這里有自定義對象Person
,構(gòu)造如下:
public class Person { private String name; private int age; private float height; }
欲將Person
作為HashMap的key
,放入哈希表中存儲(chǔ)信息。我們來探討一下,為什么要同時(shí)實(shí)現(xiàn)hashCode、equals
方法吧~
Person p1 = new Person("ciusyan", 21, 1.8f); Person p2 = new Person("ciusyan", 21, 1.8f); Map<Person, String> map = new HashMap<>(); map.put(p1, "Ciusyan"); map.put(p2, "Zhiyan");
首先要明確:
hashCode
方法用于計(jì)算出對象的哈希值equlas
方法用于比較兩個(gè)對象是否相等
二、hashCode、equals方法都未實(shí)現(xiàn)
倘若你了解哈希表的基本構(gòu)造,可以畫出一個(gè)草圖:
我們并沒有實(shí)現(xiàn)hashCode、equals
方法,為什么還能放入哈希表中呢?
- 因?yàn)?code>JDK會(huì)有默認(rèn)實(shí)現(xiàn)
在默認(rèn)的實(shí)現(xiàn)中:
- 利用
hashCode
方法計(jì)算出的哈希值是不同的 - 利用
equals
方法比較,p1和p2
不是一個(gè)對象 - 所以放入哈希表中的大致結(jié)構(gòu)如上圖所示:
- 可能會(huì)被放入兩個(gè)桶
(不同哈希值計(jì)算出的索引不一樣)
- 也可能會(huì)被放入一個(gè)桶
(不同哈希值也可能會(huì)計(jì)算出相同的索引)
,又因?yàn)槭遣煌瑢ο?,所以?huì)被串起來
- 可能會(huì)被放入兩個(gè)桶
三、只實(shí)現(xiàn)hashCode方法
如果我們實(shí)現(xiàn)了hashCode
方法,會(huì)有什么不同呢?
@Override public int hashCode() { int hash = Integer.hashCode(age); hash = hash * 31 + Float.hashCode(height); hash = hash * 31 + (name == null ? 0 : name.hashCode()); return hash; }
如上實(shí)現(xiàn),既滿足了盡量用的所有信息,也使計(jì)算的值盡量唯一了
如果是現(xiàn)在,我們再來畫一幅草圖:
現(xiàn)在只實(shí)現(xiàn)了hashCode
方法:
- 利用
hashCode
方法計(jì)算出的哈希值是相同的 equals
方法是默認(rèn)實(shí)現(xiàn),p1和p2
不是一個(gè)對象- 所以放入哈希表中的大致結(jié)構(gòu)如上圖所示:
- 只會(huì)被放入一個(gè)桶(相同的哈希值計(jì)算出的索引相同),又因?yàn)槭遣煌瑢ο?,所以?huì)被串起來
四、只實(shí)現(xiàn)equals方法
如果我們實(shí)現(xiàn)了equals
方法,會(huì)有什么不同呢?
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || o.getClass() != getClass()) return false; Person p = (Person) o; return p.age == age && p.height == height && (Objects.equals(name, p.name)); }
如上實(shí)現(xiàn),如果兩個(gè)對象的age、name、height
都相等,那么可以認(rèn)為是同一個(gè)對象
如果是現(xiàn)在:
現(xiàn)在只實(shí)現(xiàn)了equals
方法:
hashCode
方法是默認(rèn)實(shí)現(xiàn),計(jì)算出的哈希值是不同的- 利用
equals
方法比較,p1和p2
是同一個(gè)對象 - 所以放入哈希表中的大致結(jié)構(gòu)如上圖所示:
- 可能會(huì)被放入兩個(gè)桶
(計(jì)算出的索引不一樣)
- 也可能會(huì)被放入一個(gè)桶
(不同哈希值也可能會(huì)計(jì)算出相同的索引)
,又因?yàn)槭峭粚ο螅詐2的鍵和值會(huì)覆蓋掉p1的
- 可能會(huì)被放入兩個(gè)桶
五、hashCode、equals方法都實(shí)現(xiàn)
倘若我們用上面的實(shí)現(xiàn)方式,將hashCode和equals
方法都實(shí)現(xiàn)了
來看看最終的結(jié)構(gòu):
現(xiàn)在hashCode、equals
方法都實(shí)現(xiàn)了:
- 利用
hashCode
方法計(jì)算出的哈希值是相同的 - 利用
equals
方法比較,p1和p2
是同一個(gè)對象
所以放入哈希表中的大致結(jié)構(gòu)如上圖所示:
- 只會(huì)被放入一個(gè)桶中
(相同的哈希值計(jì)算出的索引相同)
,又因?yàn)槭峭粚ο?,所以p2的鍵和值會(huì)覆蓋掉p1
六、總結(jié)
如果你想要用自定義對象作為HashMap的key
,為什么hashCode、equals
方法都要實(shí)現(xiàn)?
相信你看完了四種情況,應(yīng)該能說出個(gè)balabala...
那我們一起來balabala一下吧~
- 先利用
hashCode
方法計(jì)算出哈希值:- 如果哈希值相同,在哈希表中計(jì)算出的索引肯定相同,會(huì)被放入一個(gè)桶中。這時(shí)候用
equals
方法查看是否是相同的對象。- 如果是,用新的鍵和值覆蓋掉舊的;
- 如果不是就用鏈地址法將對象串起來
- 如果哈希值不同,在哈希表中計(jì)算的索引也可能相同,也就是可能會(huì)被放入一個(gè)桶,也可能會(huì)被放入兩個(gè)桶。
- 如果被放入一個(gè)桶中,同上一樣,檢查
equlas
方法; - 如果放入兩個(gè)桶中,則不需要查看是否
equals
- 如果被放入一個(gè)桶中,同上一樣,檢查
- 如果哈希值相同,在哈希表中計(jì)算出的索引肯定相同,會(huì)被放入一個(gè)桶中。這時(shí)候用
一般的開發(fā)需求會(huì)是第四種,想要用p1和p2
作為key
存儲(chǔ)數(shù)據(jù),會(huì)認(rèn)為它們是同一個(gè)對象,它們是同一個(gè)key
,也就只會(huì)存儲(chǔ)一份數(shù)據(jù)。所以如果不同時(shí)實(shí)現(xiàn)hashCode、equals
方法,會(huì)有圖中的種種問題。
以上就是Java開發(fā)HashMap key必須實(shí)現(xiàn)hashCode equals方法原理的詳細(xì)內(nèi)容,更多關(guān)于Java開發(fā)HashMap key的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring自動(dòng)裝配之方法、構(gòu)造器位置的自動(dòng)注入操作
這篇文章主要介紹了Spring自動(dòng)裝配之方法、構(gòu)造器位置的自動(dòng)注入操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Spring?Cloud?Alibaba微服務(wù)組件Sentinel實(shí)現(xiàn)熔斷限流
這篇文章主要為大家介紹了Spring?Cloud?Alibaba微服務(wù)組件Sentinel實(shí)現(xiàn)熔斷限流過程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06詳解springMVC之與json數(shù)據(jù)交互方法
本篇文章主要介紹了詳解springMVC之與json數(shù)據(jù)交互方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05SpringMVC自定義參數(shù)綁定實(shí)現(xiàn)詳解
這篇文章主要介紹了SpringMVC自定義參數(shù)綁定實(shí)現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11IntelliJ IDEA設(shè)置代碼的快捷編輯模板Live Templates
今天小編就為大家分享一篇關(guān)于IntelliJ IDEA設(shè)置代碼的快捷編輯模板Live Templates,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-10-10IDEA 創(chuàng)建一個(gè)Mybatis Maven項(xiàng)目的方法步驟(圖文)
這篇文章主要介紹了IDEA 創(chuàng)建一個(gè)Mybatis Maven項(xiàng)目的方法步驟(圖文),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03