Java開發HashMap key必須實現hashCode equals方法原理

一、問題引入

平時在開發中,相信你多多少少都使用過HashMap,而當你用自定義對象作為key時,很多人會告訴你:你必須要同時實現自定義對象的hashCode、equals方法,否者可能會出問題,於是你就實現瞭…

可是為什麼呢?比如這裡有自定義對象Person,構造如下:

public class Person {
    private String name;
    private int age;
    private float height;
}

欲將Person作為HashMap的key,放入哈希表中存儲信息。我們來探討一下,為什麼要同時實現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方法用於計算出對象的哈希值
  • equlas方法用於比較兩個對象是否相等

二、hashCode、equals方法都未實現

倘若你瞭解哈希表的基本構造,可以畫出一個草圖:

我們並沒有實現hashCode、equals方法,為什麼還能放入哈希表中呢?

  • 因為JDK會有默認實現

在默認的實現中:

  • 利用hashCode方法計算出的哈希值是不同的
  • 利用equals方法比較,p1和p2不是一個對象
  • 所以放入哈希表中的大致結構如上圖所示:
    • 可能會被放入兩個桶(不同哈希值計算出的索引不一樣)
    • 也可能會被放入一個桶(不同哈希值也可能會計算出相同的索引),又因為是不同對象,所以會被串起來

三、隻實現hashCode方法

如果我們實現瞭hashCode方法,會有什麼不同呢?

    @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;
    }

如上實現,既滿足瞭盡量用的所有信息,也使計算的值盡量唯一瞭

如果是現在,我們再來畫一幅草圖:

現在隻實現瞭hashCode方法:

  • 利用hashCode方法計算出的哈希值是相同的
  • equals方法是默認實現,p1和p2不是一個對象
  • 所以放入哈希表中的大致結構如上圖所示:
    • 隻會被放入一個桶(相同的哈希值計算出的索引相同),又因為是不同對象,所以會被串起來

四、隻實現equals方法

如果我們實現瞭equals方法,會有什麼不同呢?

    @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));
    }

如上實現,如果兩個對象的age、name、height都相等,那麼可以認為是同一個對象

如果是現在:

現在隻實現瞭equals方法:

  • hashCode方法是默認實現,計算出的哈希值是不同的
  • 利用equals方法比較,p1和p2是同一個對象
  • 所以放入哈希表中的大致結構如上圖所示:
    • 可能會被放入兩個桶(計算出的索引不一樣)
    • 也可能會被放入一個桶(不同哈希值也可能會計算出相同的索引),又因為是同一對象,所以p2的鍵和值會覆蓋掉p1的

五、hashCode、equals方法都實現

倘若我們用上面的實現方式,將hashCode和equals方法都實現瞭

來看看最終的結構:

現在hashCode、equals方法都實現瞭:

  • 利用hashCode方法計算出的哈希值是相同的
  • 利用equals方法比較,p1和p2是同一個對象

所以放入哈希表中的大致結構如上圖所示:

  • 隻會被放入一個桶中(相同的哈希值計算出的索引相同),又因為是同一對象,所以p2的鍵和值會覆蓋掉p1

六、總結

如果你想要用自定義對象作為HashMap的key,為什麼hashCode、equals方法都要實現?

相信你看完瞭四種情況,應該能說出個balabala…

那我們一起來balabala一下吧~

  • 先利用hashCode方法計算出哈希值:
    • 如果哈希值相同,在哈希表中計算出的索引肯定相同,會被放入一個桶中。這時候用equals方法查看是否是相同的對象。
      • 如果是,用新的鍵和值覆蓋掉舊的;
      • 如果不是就用鏈地址法將對象串起來
    • 如果哈希值不同,在哈希表中計算的索引也可能相同,也就是可能會被放入一個桶,也可能會被放入兩個桶。
      • 如果被放入一個桶中,同上一樣,檢查equlas方法;
      • 如果放入兩個桶中,則不需要查看是否equals

一般的開發需求會是第四種,想要用p1和p2作為key存儲數據,會認為它們是同一個對象,它們是同一個key,也就隻會存儲一份數據。所以如果不同時實現hashCode、equals方法,會有圖中的種種問題。

以上就是Java開發HashMap key必須實現hashCode equals方法原理的詳細內容,更多關於Java開發HashMap key的資料請關註WalkonNet其它相關文章!

推薦閱讀: