1. 程式人生 > >HashMap是如何實現的(底層原理)?

HashMap是如何實現的(底層原理)?

public V put(K key, V value) {     return putVal(hash(key), key, value, false, true); //此處引數內hash(key),實際上是獲得key的hashCode值 } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {// onlyIfAbsent值為false的時候表示直接覆蓋已存在的key的value值,為true時oldValue為null則才會覆蓋     Node<K, V>[] tab; // 宣告一個tab陣列
    Node<K, V> p; // 宣告一個節點Node鍵值對     int n, i; // n表示tab的size; 表示即將把值put到某個Node(key-value)的index     if ((tab = table) == null || (n = tab.length) == 0)         n = (tab = resize()).length;                 // 如果tab為空或者長度為0時,就初始化tab(初始長度為16),並將長度值賦給n     if ((p = tab[i = (n - 1) & hash]) == null
) // 當(n - 1) & hash 位與運算的結果為key對應的index,當為控的時候         tab[i] = newNode(hash, key, value, null); // 則呼叫newNode方法新建一個Node物件賦值給tab[i]     else { // 進入此else的程式碼塊,則表示tab對應index必定已存在Node物件         Node<K, V> e; K k; // 泛型 物件k 代表key     if (p.hash == hash&& ((k = p.key) == key || (key != null
&& key.equals(k))))// 當對應index上的Node的hash值與傳入進來的key的hash值相等並且Node的key和傳入的key也相等時則覆蓋value值         e = p;     else if (p instanceof TreeNode) // 當上面不成立的時候,則說明此Node可能是紅黑樹或者連結串列. 當 是紅黑樹的時候         e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value); // 當為紅黑樹時,呼叫putTreeVal方法,此方法內部會遍歷紅黑樹,如果已存在則返回被覆蓋的Node,否則返回null     else {  // 當為連結串列時         for (int binCount = 0;; ++binCount) { // 迴圈遍歷連結串列     if ((e = p.next) == null) { // 當p.next為空時         p.next = newNode(hash, key, value, null);// 則newNode方法新建一個Node物件賦值給p.next         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st // 當連結串列達到一定長度(預設8)後,呼叫treeifyBin方法轉換成紅黑樹的結構     treeifyBin(tab, hash); // 當遍歷到p.next為空時 則表示找到了可以放置put進來的key-value的地方,所以要結束連結串列遍歷     }     if (e.hash == hash&& ((k = e.key) == key || (key != null && key.equals(k)))) // 當p.next不為空時,並且hash值和傳入的hash值相等,key值也相等時,則覆蓋值,結束迴圈         break; // 則表示覆蓋值,結束迴圈 p = e; //因為連結串列的遍歷是 p.next.next.next...這種形式,所以最後需要將e賦給p  }      }      if (e != null) { // existing mapping for key //此處是真正執行覆蓋操作的地方         V oldValue = e.value;         if (!onlyIfAbsent || oldValue == null) //onlyIfAbsent傳入為false,所以會直接覆蓋e原來的value值     e.value = value; afterNodeAccess(e); //afterNodeAccess此方法下面沒有程式碼所以無實際意義 return oldValue; // 這個地方可以看出 當我們put重複的key-value時 會返回被覆蓋的oldValue,雖然我們一般不關心put時候的返回值       }     }     ++modCount; //modCount是統計map被修改次數的      if (++size > threshold) //當tab的size大於閾值的時候則需要呼叫resize方法進行擴容          resize();afterNodeInsertion(evict); //afterNodeInsertion此方法下面沒有程式碼所以無實際意義      return null; //當put的時候不存在覆蓋值的時候則返回null }