細品Java8中hashCode方法的使用
簡介
雜湊函式(英語:Hash function)又稱雜湊演算法、雜湊函式,是一種從任何一種資料中建立小的數字“指紋”的方法。雜湊函式把訊息或資料壓縮成摘要,使得資料量變小,將資料的格式固定下來。
Java語言對hashCode的應用
主要用途
- hashcode是Object中的函式,所有類都擁有的一個函式,主要返回每個物件的hash值,主要用於雜湊表中,如HashMap、HashTable、HashSet。
- 在這裡需要注意的是,他就是為了在一些物件數組裡面儲存的時候可以節省空間。(我在這裡一直有個誤會,就是hashCode 也會應用於物件的比較,主要比較的是物件的是否有被改變過,其實我們在進行比較的時候可以不進進行重寫hashCode,單個的equals就可以保證這個物件是否相等。
- 但是很多面試官都會問到,你重寫了equals 不重寫hashcode 可以嗎?不一定,當你重寫的equals是那種兩個物件所有值都相等的情況下的時候,我們就不需要重寫。因為這樣他就符合我們的正常邏輯,就是equals相等hashcode值一定相等。但是如果你的equals定義是隻要這個物件中某個值相等就代表,這個物件相等,那麼傳統觀念就被打破了。所以你就得按照你的equals來重寫你的hashcode。保持一致。
Java 中hashcode儲存的位置
儲存在物件頭markWord,如下圖(深入理解Java虛擬機器)
我們知道了他是儲存的位置,那他是什麼時候儲存進去的呢? 在Java中所有的物件都是有hashcode嗎?
Java中HashCode的實現:
在Java中Object.class中有hashCode方法,方法是native 方法,實現就是在JVM中實現的,也就是說他是使用C語言實現的。
實現方式:OpenJDK8 預設hashCode的計算方法是通過和當前執行緒有關的一個隨機數+三個確定值,運用Marsaglia's xorshift scheme隨機數演算法得到的一個隨機數。和物件記憶體地址無關。三個確定確定值分別是:
// thread-specific hashCode stream generator state - Marsaglia shift-xor form //隨機數 _hashStateX = os::random() ; //確定值1 _hashStateY = 842502087 ; //確定值2 _hashStateZ = 0x8767 ; // (int)(3579807591LL & 0xffff) //確定值3 _hashStateW = 273326509 ;
可以通過在JVM啟動引數中新增-XX:hashCode=4,改變預設的hashCode計算方式。
為什麼要重寫hashCode
如上文提到,我們不按傳統規則重寫了equals方法,所以為了不違反規則也就得重寫hashCode。
原始碼中hashcode的重寫,如hashMap中
如果m1.entrySet( ).equals(m2.entrySet()),則兩個對映m1和 m2表示相同的對映 。這樣可確保 equals方法可在Map介面的不同實現中正常工作。
static <K,V> boolean equals(Map<K,V> source,Object object) { if (source == object) { return true; } else if (source != null && object instanceof Map) { final Map<K,V> map = (Map<K,V>) object; if (source.size() != map.size()) { return false; } else { try { return source.forAll(map::contains); } catch (ClassCastException e) { return false; } } } else { return false; } }
對映的雜湊碼定義為對映的entrySet()檢視中每個條目的雜湊碼之和 。這確保了m1.equals(m2) 隱含了對任何兩個對映 m1和m2的m1.hashCode()== m2.hashCode(),這是的總合同要求的 。Object.hashCode()
@Override public int hashCode() { return Collections.hashUnordered(this); } // hashes the elements regardless of their order static int hashUnordered(Iterable<?> iterable) { return hash(iterable,(acc,hash) -> acc + hash); }
注意點 hashMap重寫hashCode 和 計算hash桶位置的是不同的,這兩個可不敢弄混了,我是弄混了。 下來我們再看看hash桶下表的計算。jdk 1.8中的。
/ ** *計算key.hashCode()並將(XOR)雜湊的較高位*擴充套件到較低位。 * 因為該表使用2的冪次掩碼,所以*僅在當前掩碼上方的位中發生變化的*雜湊集將**總是發生衝突。 (眾所周知的示例是Float鍵集*在小表中儲存連續的整數。) *因此,我們*應用了一種變換,向下擴充套件了較高位的影響。在速度,效用和位元擴充套件*質量之間需要權衡。由於許多常見的雜湊集*已經合理地分佈了(因此不能從*擴充套件*中受益),並且由於我們使用樹來處理bin中的大量*衝突集,因此我們僅以*最便宜&的方式對一些移位後的位進行XOR運算,減少系統損失,以及*合併最高位的影響,否則由於表的限制,這些位將永遠不會在索引計算中使用 * / static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
總結
- hashCode的簡介
- Java 中 Object.hashCode()的實現
- 為什麼要重寫hashCode()?不打破傳統規則
- HashMap中hashCode方法的重寫。
- HashMap中hash桶的hash計算。
參考
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#hashCode()
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#equals(java.lang.Object)
https://juejin.cn/post/6844903487432556551
到此這篇關於細品Java8中hashCode方法的使用的文章就介紹到這了,更多相關Java8 hashCode內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!