源碼分析(一) HashMap 源碼分析|JDK8

  HashMap是一個廣泛應用於各大JAVA平臺的最最最經常使用的數據結構。<K,V>的存儲形式使HashMap備受廣大java程序員的喜歡。JDK8中HashMap發生了很大的變化,例如:以前在JDK7中存在的indexFor方法被移除了,哈希碰撞後再也不單純的使用鏈表來解決,而是使用紅黑樹+單向鏈表來解決,以前Entry<K,V>也改換爲Node<K,V>。對於紅黑樹我以爲這個是稍微有些複雜的,包括插入和刪除,紅黑樹的旋轉和5個基本原則我以爲這個是須要花時間來慢慢整理的。對數據結構的理解必定是關鍵中的關鍵。使用紅黑二叉樹的主要目的就是提高了檢索的效率,O(N)->O(logN)java

  下面就我本身對HashMap的理解作一些總結。如下代碼均來自jdk 1.8.0_73node

  1. 基礎部分

  (1)組成結構:通俗的說,HashMap就是結合了哈希表和鏈表的一種實現,在JDK8以前,之因此採用鏈表的目的實際上是在於方便解決哈希碰撞帶來的影響,提升hash值相同的插入速度。可是當一個桶轉化爲一個鏈表的時候,效率仍是會有所降低。在JDK8以後採用鏈表+紅黑樹的方式來解決哈希碰撞帶來的問題HashMap底層維護一個數組,數組中的每一項都是一個Node<K,V>。程序員

1 transient Node<K,V>[] table;//哈希表
2
3 transient Set<Map.Entry<K,V>> entrySet;//緩存

  咱們向 HashMap中所存儲的數據其實是存儲在該數組當中,而HashMap中的key,value則是以Node<K,V>的形式存放在數組中。而這個Node應該放在數組的哪個位置上(這個位置一般稱爲位桶或者hash桶,即hash值相同的Entry會放在同一位置,用鏈表相連或是紅黑樹),是經過key的hashCode來計算的。數組

1 static final int hash(Object key) {
2         int h;
3         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
4 }

  Map的put方法:其實看起來仍是有點麻煩的,源碼上put方法上的註釋提示了,若是key值相同則會替換。緩存

 1     /**
 2      * 
 3      * 
 4      * 將給定的value映射到Map上,若是該map上存在相同的key值,則會替換爲現有的值
 5      *
 6      * @param key key with which the specified value is to be associated
 7      * @param value value to be associated with the specified key
 8      * @return the previous value associated with <tt>key</tt>, or
 9      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
10      *         (A <tt>null</tt> return can also indicate that the map
11      *         previously associated <tt>null</tt> with <tt>key</tt>.)
12      */
13     public V put(K key, V value) {
14         return putVal(hash(key), key, value, false, true);
15     }

  putVal能夠劃分爲這樣的一個流程:數據結構

    1)當前哈希表是否爲空,若是爲空就初始化一個app

    2)而後插入這個value,要是這個數組沒有就插入,有的話就替代原先的valuethis

    3)要是hash碰撞了,看看當前位置是紅黑樹仍是鏈表,而後插入。一樣,要是發現key相同則進行覆蓋。spa

    4)要是當前鏈表長度超過閾值了(默認是8),就轉紅黑樹 code

  這裏要有個約定:

    1) 哈希表是一個大的數組,類型是Node<K,V>,規定這個是每一個桶,

    2)桶裏裝的是bin

    3)size是node的數量,也就是你實際存放K,V的數量

    4)capacity是桶的數量,沒規定初始化容量時,通常默認是16個

 1  final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 2                    boolean evict) {
 3         Node<K,V>[] tab; Node<K,V> p; int n, i;
 4         if ((tab = table) == null || (n = tab.length) == 0)
 5             n = (tab = resize()).length;
 6         if ((p = tab[i = (n - 1) & hash]) == null)
 7             tab[i] = newNode(hash, key, value, null);
 8         else {
 9             Node<K,V> e; K k;
10             if (p.hash == hash &&
11                 ((k = p.key) == key || (key != null && key.equals(k))))
12                 e = p;
13             else if (p instanceof TreeNode)
14                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
15             else {
16                 for (int binCount = 0; ; ++binCount) {
17                     if ((e = p.next) == null) {
18                         p.next = newNode(hash, key, value, null);
19                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
20                             treeifyBin(tab, hash);
21                         break;
22                     }
23                     if (e.hash == hash &&
24                         ((k = e.key) == key || (key != null && key.equals(k))))
25                         break;
26                     p = e;
27                 }
28             }
29             if (e != null) { // existing mapping for key
30                 V oldValue = e.value;
31                 if (!onlyIfAbsent || oldValue == null)
32                     e.value = value;
33                 afterNodeAccess(e);
34                 return oldValue;
35             }
36         }
37         ++modCount;
38         if (++size > threshold)
39             resize();
40         afterNodeInsertion(evict);
41         return null;
42     }
相關文章
相關標籤/搜索