HashMap中對象作Key爲什麼要重寫equals和hashcode

先贊後看,養成習慣 🌹 歡迎微信關注[Java編程之道],每天進步一點點,沉澱技術分享知識。

蜜汁 equals & hashcode

哈哈!我攤牌了!阿里和美團的面試官都問過我同樣的問題,你確定不瞭解一下?

今天咱們嘮點簡單的,這是在初級開發及校招面試中經常問的一個問題。

  • HashMap的key爲一個對象的時候要注意什麼 ?

  • 爲什麼要同時重寫equals和hashcode方法 ?

給你十秒鐘想想你該怎麼答… ⌚

想不出來沒關係,看了這篇文章後面試遇到同樣的問題就是送分題。📕

什麼是equals和hashcode方法

我們知道Java中所有的類都繼承於Object類及Object類是所有類的父類。當子類調用一個方法時,如果該方法沒有被重寫則需要往上面找到父類中的方法執行。

Object類

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

在Object類中,hashCode是一個本地方法簡單理解爲獲取對象地址,equals方法比較自己和obj對象地址是否相等。在這裏一定先認識到這兩個方法,一個是取地址,一個是比較地址。


下面進入正題…

對象做Key會怎樣

下面以一段小代碼演示一下,其輸出的結果是 null 。先說一句:在這裏我們認爲我的a和b是有着相同屬性的同一個對象(key),我可以通過hashMap.get(b)獲取到字符串 hello。但事與願違。

public class NoHashCodeAndEquals {
    public static void main(String[] args) {
        Object o = new Object();
        HashMap<Demo, String> hashMap = new HashMap<>();
        Demo a = new Demo("A");
        Demo b = new Demo("A");
        hashMap.put(a, "hello");
        String s = hashMap.get(b);
        System.out.println(s);

    }
}
class Demo {
    String key;

    Demo(String key) {
        this.key = key;
    }
}

可能大家一眼看過去感覺沒問題,a和b不是同一個對象呀!地址肯定不一樣!打住!你在說什麼,我剛剛說什麼來着 ? 👆 蒙圈了吧!😵(ps:我自己也快說懵圈了🤭)。

穩住!!!

咱們簡單看兩行HashMap源碼,看完立馬就清醒。

  • 獲取hashcode計算桶下標,存放元素,看上去沒什麼毛病就是計算下標而已。對!只是調用key的hashCode計算一個下標值。
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

看到了hashCode方法,咱們就還差一個equals方法,HashMap中對於equals()方法的調用在哪些位置呢?

  • put方法(以JDK1.7爲例)
public V put(K key, V value) {
...
int hash = hash(key);
// 確定桶下標
int i = indexFor(hash, table.length);
// 先找出是否已經存在鍵爲 key 的鍵值對,如果存在的話就更新這個鍵值對的值爲 value
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
...
// 插入新鍵值對
addEntry(hash, key, value, i)
return null;
}
  • get方法(以JDK1.7爲例)
public V get(Object key) {  
    if (key == null)  
        return getForNullKey();  
    int hash = hash(key.hashCode());  
    for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {  
        Object k;  
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  
            return e.value;  
    }  
    return null;  
}

如上所示:當HashMap調用插入或獲取方法,需要將值(key)對應的哈希碼與數組中的哈希碼比較,相等時,則會通過equals方法比較key值是否相等。

再結合上面我寫的代碼案例,緩一緩咱們再品一下!

Demo a = new Demo("A");
Demo b = new Demo("A");

這兩行的代碼的含義,我們理解爲定義了兩個相同含義(認爲是同一個key)的key對象,但是大家都知道這兩個key的hashcode方法的值是不一樣的。

在HashMap中的比較key是這樣的,先求出key的hashcode(),比較其值是否相等,若相等再比較equals(),若相等則認爲他們是相等的。若equals()不相等則認爲他們不相等。

  • 如果只重寫hashcode()不重寫equals()方法,當比較equals()時,其實調用的是Object中的方法,只是看他們是否爲同一對象(即進行內存地址的比較)。
  • 如果只重寫equals()不重寫hashcode()方法,在一個判斷的時候就會被攔下HashMap認爲是不同的Key。

所以想以對象作爲HashMap的key,必須重寫該對象的hashCode和equals方法。確保hashCode相等的時候equals的值也是true。

  • 圖解如下:

對於這個問題看似簡單,可很多初級程序員竟然說不出一個所以然,一方面是對HashMap不熟悉另外就是對這兩個方法的含義理解不透徹。

最後再次總結一句:在HashMap的“鍵”部分存放自定義的對象,一定要重寫equalshashCode方法。再來兩句老生常談的話!

  • 兩個對象==相等,則其hashcode一定相等,反之不一定成立。
  • 兩個對象equals相等,則其hashcode一定相等,反之不一定成立。

自己再品品呢 ~


更多精彩好文盡在:Java編程之道 🎁
歡迎各位好友前去關注!🌹
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章