一文搞定HashMap

HashMap源碼閱讀

介紹:

  • 繼承自Map.Entry<K,V>
  • 無序、允許爲null、非同步

分析

參數

  • 默認容量16

  • 最大容量2的31次方

  • 默認的填充因子0.75(太大查詢效率低,太小數組利用率低)

  • 當bucket上的結點大於8時會轉成紅黑樹

  • 當bucket上的結點小於6時會轉換爲鏈表

  • 樹化參數64,當小於64時只是簡單的擴容,大於64是會樹化

  • Node:靜態內部類,用來表示鍵值對

  • TreeNode:紅黑樹節點

常見問題

HashMap數據結構:

​ 哈希表結構(鏈表散列:數組+鏈表),結合數組和鏈表的有點,當鏈表長度超過8時,鏈表轉化爲紅黑樹

HashMap 工作原理

​ HashMap的底層是hash數組和單向鏈表實現的,數組中的每個元素都是鏈表,由Node內部類(實現Map.Entry接口)實現,HashMap通過put&get方法存儲和獲取

存儲對象的時候,將k/v傳給put()方法:

在這裏插入圖片描述

  • 初始化table:判斷table是否爲空或者爲null
  • 計算hash值:通過高16位和低16位的異或運算(讓高16位也參入進來以便減低衝突)
  • 插入或更新節點:根據(n-1)&hash計算得到插入的數組下標i然後進行判斷
    • table[i]==null:沒有hash衝突直接新建節點添加
    • table[i]!=null:判斷首個節點和key是否一樣
      • 相同:直接更新value
      • 不相同:判斷是否是紅黑樹
        • 紅黑樹:直接在樹中插入鍵值對
        • 鏈表:遍歷鏈表判斷是否已經存在此key:
          • 存在更新
          • 不存在就插入(1.8是尾插入),然後判斷是否轉爲紅黑樹
  • 擴容:根據size和數組長度*負載因子來判斷是否需要擴容

獲取對象時,將 K 傳給 get() 方法:

  • 調用 hash(K) 方法(計算 K 的 hash 值)從而獲取該鍵值所在鏈表的數組下標;
  • 順序遍歷鏈表,equals()方法查找相同 Node 鏈表中 K 值對應的 V 值。

hashCode 是定位的,存儲位置;equals是定性的,比較兩者是否相等。

爲什麼要一起重寫hashCode()和equal()方法

假設都不重寫

這樣hashMap就可以存在key相同的值

只重寫hashCode

無法正確地與鏈表元素進行相等判斷,從而無法保證去重

只重寫equals()方法

映射到HashMap中不同數組下標,無法保證去重

當兩個對象的hashCode相同會發生什麼

​ 因爲hashCode相同,但是不一定就是相等的(需要根據equals方法比較),所以兩個對象所在數組的下標相同,碰撞就此發生。

hash的實現

​ 在jdk1.8中,是通過hashCode()的高16位異或低16位實現的:(h=k.hashCode())^h(>>>16),主要從速度、功效和質量來考慮的,減少系統開銷,也不會造成因爲高位沒有參數下標的計算從而引起的碰撞

使用異或運算符

​ 保證了對象的hashCode32位值只要有一位發生改變,整個hash()返回值就會改變,儘可能減少碰撞

數組擴容的過程

​ 創建一個新的數組,其容量爲舊數組的兩倍,並重新計算舊數組中結點的存儲位置。

​ 結點在新數組中的存儲位置只有兩種:原下標或者原下標+舊數組的大小

拉鍊法導致鏈表過深問題爲什麼使用紅黑樹不選擇二叉樹,爲啥不一直使用紅黑樹

​ 因爲二叉查詢樹在特殊情況下回編程一條線性結構(造成一樣的問題),導致遍歷查詢緩慢,而紅黑樹可以解決這個問題。

​ 紅黑樹在插入新數據後,可能需要左旋、右旋、變色這些操作來保持平衡,但是爲了保持平衡需要付出一些代價,所以在鏈表長度較小(8)時,不使用紅黑樹

紅黑樹的介紹

  • 每個結點非紅即黑
  • 根節點總是黑色的
  • 如果節點是紅色的,則其子節點必須是黑色的
  • 每個葉子節點都是黑色的空節點(NIL節點)
  • 從根節點到葉子節點或空節點的每條路徑,必須包含相同數目的黑色節點

HashMap、LinkedHashMap、TreeMap有什麼區別與使用場景

HashMap:(使用最多)

  • 區別:
  • 使用場景:在map中插入、刪除和定位元素

LinkedHashMap:

  • 區別:保存了記錄的插入順序,在用Iterator遍歷時,先取到的記錄肯定是先插入的,遍歷比HashMap慢
  • 使用場景:在需要輸出的順序和輸入的順序相同的情況下

TreeMap:

  • 區別:實現SortMap接口,能夠把它保存的記錄根據鍵排序(默認升序)
  • 使用場景:需要按自然順序或自定義順序遍歷鍵的情況

HashMap和HashTable區別

  • HashMap是線程不安全的,HashTable是線程安全的,導致效率也低下
  • HashMap最多隻允許一條記錄的鍵爲null,與韓旭旭多條記錄的值爲null,而HashTable不允許
  • HashMap默認初始化數組大小爲16,擴容爲兩倍、HashTable爲11,擴容爲兩倍+1

最後

  • 如果覺得看完有收穫,希望能給我點個贊,這將會是我更新的最大動力,感謝各位的支持
  • 歡迎各位關注我的公衆號【java冢狐】,專注於java和計算機基礎知識,保證讓你看完有所收穫,不信你打我
  • 如果看完有不同的意見或者建議,歡迎多多評論一起交流。感謝各位的支持以及厚愛。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章