hashCode方法
簡介
散列函式(英語:Hash function)又稱散列演算法、哈希函式,是一種從任何一種資料中創建小的數字“指紋”的方法,散列函式把訊息或資料壓縮成摘要,使得資料量變小,將資料的格式固定下來,
Java語言對hashCode的應用
3. 主要用途
- hashcode是Object中的函式,所有類都擁有的一個函式,主要回傳每個物件的hash值,主要用于哈希表中,如HashMap、HashTable、HashSet,
- 在這里需要注意的是,他就是為了在一些物件陣列里面存盤的時候可以節省空間,(我在這里一直有個誤會,就是hashCode 也會應用于物件的比較,主要比較的是物件的是否有被改變過,其實我們在進行比較的時候可以不進進行重寫hashCode,單個的equals就可以保證這個物件是否相等,
- 但是很多面試官都會問到,你重寫了equals 不重寫hashcode 可以嗎?不一定,當你重寫的equals是那種兩個物件所有值都相等的情況下的時候,我們就不需要重寫,因為這樣他就符合我們的正常邏輯,就是equals相等hashcode值一定相等,但是如果你的equals定義是只要這個物件中某個值相等就代表,這個物件相等,那么傳統觀念就被打破了,所以你就得按照你的equals來重寫你的hashcode,保持一致,
4. Java 中hashcode存盤的位置
-
存盤在物件頭markWord,如下圖(深入理解Java虛擬機)

-
我們知道了他是存盤的位置,那他是什么時候存盤進去的呢? 在Java中所有的物件都是有hashcode嗎?
5. 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計算方式,
6. 為什么要重寫hashCode
- 如上文提到,我們不按傳統規則重寫了equals方法,所以為了不違反規則也就得重寫hashCode,
2.原始碼中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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/229155.html
標籤:java
下一篇:JAVA-泛型
