參考:http://www.cnblogs.com/joemsu/p/7724623.htmlgit
jdk1.8:數組、鏈表/紅黑樹(jdk1.7的是數組+鏈表)github
節點數據類型:
Node<K,V>(hash,key,value,next)數組
static class Node<K,V> implements Map.Entry<K,V> { // key & value 的 hash值 final int hash; final K key; V value; //指向下一個節點 Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } public final K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + "=" + value; } public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { Map.Entry<?,?> e = (Map.Entry<?,?>)o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) return true; } return false; } }
/默認初始化map的容量:16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //map的最大容量:2^30 static final int MAXIMUM_CAPACITY = 1 << 30; //默認的填充因子:0.75,能較好的平衡時間與空間的消耗 static final float DEFAULT_LOAD_FACTOR = 0.75f; //將鏈表(桶)轉化成紅黑樹的臨界值 static final int TREEIFY_THRESHOLD = 8; //將紅黑樹轉成鏈表(桶)的臨界值 static final int UNTREEIFY_THRESHOLD = 6; //轉變成樹的table的最小容量,小於該值則不會進行樹化 static final int MIN_TREEIFY_CAPACITY = 64; //上圖所示的數組,長度老是2的冪次 transient Node<K,V>[] table; //map中的鍵值對集合 transient Set<Map.Entry<K,V>> entrySet; //map中鍵值對的數量 transient int size; //用於統計map修改次數的計數器,用於fail-fast拋出ConcurrentModificationException transient int modCount; //大於該閾值,則從新進行擴容,threshold = capacity(table.length) * load factor int threshold; //填充因子 final float loadFactor;
根據key的hashCode計算在數組裏的下標,而後再判斷該位置是否符合條件,不然根據鏈表/紅黑樹的方法繼續找安全
計算下標的方法:
下標 = (n-1) & hash
hash = (h = key.hashCode()) & h>>>16數據結構
判斷兩個key是否相同:性能
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
先判斷hash,若是hash不一樣必定不一樣;若是hash相同,則利用equals判斷是否相同this
先檢查容量(擴容resize)。
根據hashCode計算桶在數組裏的位置,
插入該處的鏈表/樹線程
兩倍容量。若是容量已達最大值,則設置容量爲int_max。
擴容以後要從新計算index,每一個桶裏的節點都要從新計算位置,計算位置的方法是hash & (n-1)。
擴容以後因爲newCap-1比oldCap-1多了一個高位(例如8-1=7=111,而4-1=3=11),所以節點的新位置取決於多出來的一個高位bit,若是該位是0,則仍是原位置;不然應該是原位置+舊容量code
擴容的過程對整個數組和桶進行了遍歷,耗費性能,因此最好在建立HashMap的時候指定容量,不然在第一次put的時候會進行一次擴容(由於table==null)