先贊後看,養成習慣 🌹 歡迎微信關注
[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的“鍵”部分存放自定義的對象,一定
要重寫equals
和hashCode
方法。再來兩句老生常談的話!
- 兩個對象==相等,則其hashcode一定相等,反之不一定成立。
- 兩個對象equals相等,則其hashcode一定相等,反之不一定成立。
自己再品品呢 ~
更多精彩好文盡在:Java編程之道
🎁
歡迎各位好友前去關注!🌹