EFAULT_LOAD_FACTOR是默認的負載因子=0.75;
MAXIMUM_CAPACITY是最大HashMap容量;
threshold爲一個閾值大小是(capacity * load factor)即容量*負載因子;
程序員
1)方法一: public HashMap()
無參構造函數會默認構造出一個初始容量爲16,負載因子爲0.75的空HashMap;
數組
2) 方法二:public HashMap(int initialCapacity, float loadFactor)
第二種構造方法會構造出一個初始容量大於或者等於initialCapacity的2的冪次方,負載因子爲loadFactor空HashMap;
bash
3)方法三:public HashMap(int initialCapacity)
方法三調用方法二,默認負載因子爲0.75;
微信
4)方法四:public HashMap(Map<? extends K, ? extends V> m)
數據結構
方法四構造一個和指定的Map有相同的Mappings的HashMap,默認的負載因子爲0.75;
app
源碼:
函數
tableSizeFor方法返回大於initialCapacity的最近的2的冪次方值。 從源碼中咱們看到n獲得的是cap-1,這步操做主要是爲了防止cap已是2的冪次方的狀況, 後面一系列n |= n>>> 一、二、四、五、16;無符號右移操做,使得n的二進制低位全爲1, 最後判斷n是否大於MAXIMUM_CAPACITY,大於則返回MAXIMUM_CAPACITY,不然返回n+1即爲2的冪次方。
源碼分析
源碼:
ui
putMapEntries()的功能爲將傳入的Map初始化爲一個HashMap結構, 轉化前先判斷m的大小,若是爲0則不必初始化,不然將Map中的元素一次將其放進HashMap, 此時分爲兩種狀況: <1>table沒有初始化 在這種狀況下,咱們須要檢查map的大小是否超過MAXIMUM_CAPACITY,以後初始化threshold爲相應的大小。 <2>table已經初始化 這種狀況下須要檢查s是否大於當前的threshold閾值,若是大於則resize(),不然不需擴容,只需將m中每一個元素放進HashMap便可。
this
putMapEntries中的resize()
resize就是從新將原來HashMap內的數據放進一個新擴容的HashMap中,
而舊容器中的數據存放位置有兩種狀況,
<1>在原來的位置上
<2>在原來的位置+oldCap的位置上
當咱們HashMap中的數據量達到threshold時,便可進行resize()擴容;
源碼:
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
} 複製代碼
哈希衝突簡單來說就是兩次數據通過一系列計算得到的哈希值相同,
哈希衝突有三種狀況:
<1>當兩個節點的key值相同時,此時必定發生哈希衝突,
此時將key相應的value值換成新值
<2>當兩個節點key值不一樣,但由於哈希函數的運算致使最後的哈希值相同致使的衝突,
此時將value值放進此哈希值內單鏈表或者紅黑樹中
<3>兩節點key值不一樣,哈希值不一樣,可是對數組長度取模後獲得的值相同
遵循狀況2的處理方式
首先從實現的接口來看HashMap和Hashtable都實現了Map、Cloneable(可複製)、Serializable(可序列化)這三個接口,
而HashMap是繼承自AbstractMap類,HashTable是繼承自Dictionary類。
補充:
1.sychronized意味着在一次僅有一個線程可以更改Hashtable。就是說任何線程要更新Hashtable時要首先得到同步鎖,其它線程要等到同步鎖被釋放以後才能再次得到同步鎖更新Hashtable。
2.HashMap中,null能夠做爲鍵,這樣的鍵只有一個;能夠有一個或多個鍵所對應Value值爲null。
3.由於HashTable自己有synchronized因此意味着一次只能有一個線程改變HashTable 4.HashMap首先須要根據元素的 KEY計算出一個hash值,而後再用這個hash值來計算獲得最終的位置。 Hashtable直接使用對象的hashCode。hashCode是JDK根據對象的地址或者字符串或者數字算出來的int類型的數值。而後再使用除留餘數發來得到最終的位置。
更多技術文章請關注微信公衆號:Java程序員彙集地