現在的位置: 首頁 > 架構設計 > 正文

HashMap 中的 put 方法有哪些操作?

2020年02月19日 架構設計 ⁄ 共 1229字 ⁄ 字型大小 評論關閉

  HashMap 想必是平時工作中使用非常的頻繁的數據結構了吧,它簡單易用,同時也是面試掃盲第一輪技術面中出現頻率極高的題目之一。假設我問你:HashMap 一次 put 操作的詳細過程是怎樣的,你可以對答如流嗎?

  HashMap 的存儲結構就不用多介紹了,底層是一個列表/二叉樹的數組結構。當我們調用一次put操作的時候主要會經過如下4個大步驟,讓我們來詳細看一下這四個步驟都做了些什麼事情。

  1. 求 Key 的 hash 值

  想要存入 HashMap 的元素的 key 都必須要經過一個 hash 運算,首先調用 key 對象的 hashCode 方法獲取到對象的 hash 值。然後將獲得 hash 值向右位移 16 位,接著將位移後的值與原來的值做異或運算,即可獲得到一個比較理想的 hash 值,之所以這樣運算時為了儘可能降低 hash 碰撞(即使得元素均勻分布到各個槽中)。

  2. 確定 hash 槽

  獲取到 hash 值後就是計算該元素所對應的 hash 槽,這一步操作比較簡單。直接將上一步操作中獲取到的 hash 值與底層數組長度取模(為了提高性能,源碼採用了運算,這樣等價於取模運算,但是性能更好)獲取 index 位置。

  3. 將元素放入槽中

  在上一步中我們獲得了 hash 槽的索引,接下來就是將元素放入到槽中,如果槽里當前沒有任何元素則直接生成 Node 放入槽中,如果槽中已經有元素存在,則會有下面 2 種情況:

  3.1 槽里是一顆平衡二叉樹

  當列表的元素長度超過 8 時,為了加快槽上元素的索引速度,HashMap 會將列錶轉換為平衡二叉樹。所以在這種情況下,插入元素是在一顆平衡二叉樹上做操作,查找和更新的時間複雜度都是 log(n),HashMap 會將元素包裝為一個 TreeNode 添加到樹上。具體平衡二叉樹的操作就不在此展開了,有興趣的小夥伴可自行探索。

  3.2 槽里是一個列表

  初始情況下,槽裡面的元素是以列表形式存在的,HashMap 遍歷列表將元素更新/追加 到列表尾部。元素添加後,HashMap 會判斷當前列表元素個數,如達到 8 個元素則將列錶轉化為平衡二叉樹,具體轉換詳情可參考 HashMap 中的方法 `final void treeifyBin(Node[] tab, int hash)` 。

  4. 擴容

  到這裡時候,我們元素已經完美的放到了 HashMap 的存儲中。但是還有一個 HashMap 的自動擴容操作需要完成,默認情況下自動擴容因子是 0.75,即當容量為 16 時候,如果存儲元素達到 16 * 0.75 = 12 個的時候,HashMap 會進行 resize 操作(俗稱 rehash)。HashMap 會新建一個 2 倍容量與舊數組的數組,然後依次遍歷舊的數組中的每個元素,將它們轉移到新的數組當中。其中有 2 個需要注意的點:列表中元素依然會保持原來的順序和加入二叉樹上的元素少達 6 個時候會將二叉樹再次轉化為列表來存儲。

  如上我們介紹了 HashMap 的依次 put 操作的全過程,小夥伴們快去看源碼鞏固一下吧,歡迎留言與我交流哦。

抱歉!評論已關閉.