第一:HashMap的概念
<span style="font-size:14px;">HashMap 基於哈希表的 Map 接口的實現。此實現提供所有可選的映射操作,並允許使用 null 值和 null 鍵。(除了非同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證映射的順序,特別是它不保證該順序恆久不變。</span>
第二:什麼是哈希表?
<span style="font-size:14px;"> 散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度,這個映射函數叫做散列函數,存放記錄的數組叫做散列表 PS:給定表M,存在函數f(key),對任意給定的關鍵字值key,代入函數後若能得到包含該關鍵字的記錄在表中的地址,則稱表M爲哈希(Hash)表,函數f(key)爲哈希(Hash) 函數。</span>
<span style="font-size:14px;">
</span>
第三:什麼是HashCode?
<span style="font-size:18px;">hashCode是jdk根據對象的地址或者字符串或者數字算出來的int類型的數值。</span>
<span style="font-size:14px;">
</span>
java Hash Map 就是根據對象的hashCode的值,然後通過散列函數計算出存放的地址(也就是索引值)。但是如果遇到計算出來的值相同的話,這種情況叫做哈希衝突,ps:y = x % H ,如果 x = object.hashCode();
則y 計算出來的值 就可以能相同,當然出現社種衝突的時候,我們一般可以有以下方法解決。- 開放地址法
開放地執法有一個公式:Hi=(H(key)+di) MOD m i=1,2,...,k(k<=m-1)
其中,m爲哈希表的表長。di 是產生衝突的時候的增量序列。如果di值可能爲1,2,3,...m-1,稱線性探
測再散列。
如果di取1,則每次衝突之後,向後移動1個位置.如果di取值可能爲1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k(k<=m/2)
稱二次探測再散列。如果di取值可能爲僞隨機數列。稱僞隨機探測再散列 - 再哈希法
當發生衝突時,使用第二個、第三個、哈希函數計算地址,直到無衝突時。缺點:計算時間增加。
比如上面第一次按照姓首字母進行哈希,如果產生衝突可以按照姓字母首字母第二位進行哈希,再衝突,第三位,直到不衝突爲止 - 鏈地址法
將所有關鍵字爲同義詞的記錄存儲在同一線性鏈表中。 - 建立一個公共溢出區
假設哈希函數的值域爲[0,m-1],則設向量HashTable[0..m-1]爲基本表,另外設立存儲空間向量OverTable[0..v]用以存儲發生衝突的記錄。
經過以上方法,基本可以解決掉hash算法衝突的問題。
第四:HashMap源碼學習
HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable
因此HashMap是實現Map接口的,同時 實現Serializable接口,所以可以序列化。每個HashMap內部都有一個HashMapEntry
數級對象。 @Override public V put(K key, V value) {
if (key == null) {
return putValueForNullKey(value);
}
int hash = secondaryHash(key.hashCode());
HashMapEntry<K, V>[] tab = table;
int index = hash & (tab.length - 1);
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
if (e.hash == hash && key.equals(e.key)) {
preModify(e);
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
// No entry for (non-null) key is present; create one
modCount++;
if (size++ > threshold) {
tab = doubleCapacity();
index = hash & (tab.length - 1);
}
addNewEntry(key, value, hash, index);
return null;
}
如果Key值 爲空,則加入putValueForNullKey(value);先計算hash 值 int hash = secondaryHash(key.hashCode()); 然後 根據 hash值計算index值 :
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
if (e.hash == hash && key.equals(e.key)) {
preModify(e);
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
遍歷數組查找是否有已經相同key存在,則更新原來的值。否則添加到數組中。addNewEntry(key,
value, hash, index) public V get(Object key) {
if (key == null) {
HashMapEntry<K, V> e = entryForNullKey;
return e == null ? null : e.value;
}
// Doug Lea's supplemental secondaryHash function (inlined)
int hash = key.hashCode();
hash ^= (hash >>> 20) ^ (hash >>> 12);
hash ^= (hash >>> 7) ^ (hash >>> 4);
HashMapEntry<K, V>[] tab = table;
for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
e != null; e = e.next) {
K eKey = e.key;
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
return e.value;
}
}
return null;
}
根本是根據hashCode計算index (hash
& (tab.length - 1).取相應的Entry,然後對比key值,返回value