HashMap的put、get方法詳解(附源碼)

put方法

HashMap 只提供了 put 用于添加元素,putVal 方法只是給 put 方法調用的一個方法,并沒有提供給用戶使用。
對 putVal 方法添加元素的分析如下:如果定位到的數組位置沒有元素 就直接插入。如果定位到的數組位置有元素就和要插入的 key 比較,如果 key 相同就直接覆蓋,如果 key 不相同,就判斷 p 是否是一個樹節點,如果是就調用e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value)將元素添加進入。如果不是就遍歷鏈表插入(插入的是鏈表尾部)。
在這里插入圖片描述
HashMap HashMap的put()方法用于向HashMap中添加鍵值對,當調用HashMap的put()方法時,會按照以下詳細流程執行(JDK8 1.8版本):

第一步:根據要添加的鍵的哈希碼計算在數組中的位置(索引)。
第二步:檢查該位置是否為空(即沒有鍵值對存在)

  • 如果為空,則直接在該位置創建一個新的Entry對象來存儲鍵值對。將要添加的鍵值對作為該Entry的鍵和值,并保存在數組的對應位置。將HashMap的修改次數(modCount)加1,以便在進行迭代時發現并發修改。

第三步:如果該位置已經存在其他鍵值對,檢查該位置的第一個鍵值對的哈希碼和鍵是否與要添加的鍵值對相同?

  • 如果相同,則表示找到了相同的鍵,直接將新的值替換舊的值,完成更新操作。

第四步:如果第一個鍵值對的哈希碼和鍵不相同,則需要遍歷鏈表或紅黑樹來查找是否有相同的鍵:

如果鍵值對集合是鏈表結構,從鏈表的頭部開始逐個比較鍵的哈希碼和equals()方法,直到找到相同的鍵或達到鏈表末尾。

  • 如果找到了相同的鍵,則使用新的值取代舊的值,即更新鍵對應的值。
  • 如果沒有找到相同的鍵,則將新的鍵值對添加到鏈表的頭部。

如果鍵值對集合是紅黑樹結構,在紅黑樹中使用哈希碼和equals()方法進行查找。根據鍵的哈希碼,定位到紅黑樹中的某個節點,然后逐個比較鍵,直到找到相同的鍵或達到紅黑樹末尾。

  • 如果找到了相同的鍵,則使用新的值取代舊的值,即更新鍵對應的值。
  • 如果沒有找到相同的鍵,則將新的鍵值對添加到紅黑樹中。

第五步:檢查鏈表長度是否達到閾值(默認為8):

  • 如果鏈表長度超過閾值,且HashMap的數組長度大于等于64,則會將鏈表轉換為紅黑樹,以提高查詢效率。

第六步:檢查負載因子是否超過閾值(默認為0.75):

  • 如果鍵值對的數量(size)與數組的長度的比值大于閾值,則需要進行擴容操作。

第七步:擴容操作:

  • 創建一個新的兩倍大小的數組。
  • 將舊數組中的鍵值對重新計算哈希碼并分配到新數組中的位置。
  • 更新HashMap的數組引用和閾值參數。

第八步:完成添加操作。
此外,HashMap是非線程安全的,如果在多線程環境下使用,需要采取額外的同步措施或使用線程安全的ConcurrentHashMap。

源碼如下:

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;// table未初始化或者長度為0,進行擴容if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;// (n - 1) & hash 確定元素存放在哪個桶中,桶為空,新生成結點放入桶中(此時,這個結點是放在數組中)if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);// 桶中已經存在元素(處理hash沖突)else {Node<K,V> e; K k;//快速判斷第一個節點table[i]的key是否與插入的key一樣,若相同就直接使用插入的值p替換掉舊的值e。if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;// 判斷插入的是否是紅黑樹節點else if (p instanceof TreeNode)// 放入樹中e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);// 不是紅黑樹節點則說明為鏈表結點else {// 在鏈表最末插入結點for (int binCount = 0; ; ++binCount) {// 到達鏈表的尾部if ((e = p.next) == null) {// 在尾部插入新結點p.next = newNode(hash, key, value, null);// 結點數量達到閾值(默認為 8 ),執行 treeifyBin 方法// 這個方法會根據 HashMap 數組來決定是否轉換為紅黑樹。// 只有當數組長度大于或者等于 64 的情況下,才會執行轉換紅黑樹操作,以減少搜索時間。否則,就是只是對數組擴容。if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);// 跳出循環break;}// 判斷鏈表中結點的key值與插入的元素的key值是否相等if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))// 相等,跳出循環break;// 用于遍歷桶中的鏈表,與前面的e = p.next組合,可以遍歷鏈表p = e;}}// 表示在桶中找到key值、hash值與插入元素相等的結點if (e != null) {// 記錄e的valueV oldValue = e.value;// onlyIfAbsent為false或者舊值為nullif (!onlyIfAbsent || oldValue == null)//用新值替換舊值e.value = value;// 訪問后回調afterNodeAccess(e);// 返回舊值return oldValue;}}// 結構性修改++modCount;// 實際大小大于閾值則擴容if (++size > threshold)resize();// 插入后回調afterNodeInsertion(evict);return null;
}

get方法

HashMap的get(Object key)方法用于根據指定的鍵(key)獲取其對應的值(value)。當調用此方法時,會執行與put()方法類似的定位邏輯來查找節點,但過程相對簡單,因為它不涉及修改操作。以下是詳細的執行流程(JDK 8 1.8版本):

第一步:計算哈希值,定位數組索引

  • 首先,get(key)方法會調用內部的getNode(hash(key), key)方法。
  • getNode方法中,它會根據傳入的key計算出其哈希碼(hash value)。
  • 然后,通過位運算 (n - 1) & hash(其中n是HashMap內部數組的長度)來精確計算出該key應該位于數組的哪個索引位置(即哪個桶)。

第二步:檢查桶內情況,優先檢查第一個節點。

  • 定位到數組索引后,系統會檢查該位置是否存在節點。
  • 如果該位置為null(即桶為空),則意味著Map中不存在這個keygetNode方法直接返回null
  • 如果該位置不為null,系統會進行一個快速判斷:檢查該桶中第一個節點的哈希值和key本身是否與要查找的key完全匹配(先比較hash,再用==equals()比較key)。
  • 如果第一個節點就匹配成功,則直接返回這個節點,查找結束。這是對無哈希沖突情況的優化。

第三步:處理哈希沖突,遍歷鏈表或紅黑樹。

  • 如果第一個節點不匹配,并且該節點后面還存在其他節點(即first.next != null),則說明發生了哈希沖突,需要進一步在鏈表或紅黑樹中查找。
  • 判斷數據結構:
    • 如果為紅黑樹:通過 first instanceof TreeNode 判斷。如果是,則調用紅黑樹專屬的getTreeNode()方法,在樹中進行高效查找。
    • 如果為鏈表: 則從第二個節點開始,進入一個do-while循環,逐個向后遍歷鏈表中的每個節點。
  • 遍歷查找:
    • 在遍歷鏈表或查找紅黑樹的過程中,對每個節點都進行哈希值和key的比較。
    • 如果在鏈表或紅黑樹中找到了完全匹配的節點,則立即返回該節點。
    • 如果遍歷完整個鏈表或紅黑樹仍未找到匹配項,getNode方法將返回null

第四步:返回最終結果。

  • get方法會接收getNode方法的返回值(一個Node對象或null)。
  • 如果返回的節點對象不為nullget方法會提取并返回該節點的value值。
  • 如果返回的節點為null,則get方法最終也返回null,表示在HashMap中未找到指定的key

源碼如下:

// get方法是getNode方法的封裝,它處理了getNode返回null的情況
public V get(Object key) {Node<K,V> e;// 調用getNode獲取節點,如果節點為null,則返回null,否則返回節點的valuereturn (e = getNode(hash(key), key)) == null ? null : e.value;
}/*** 實現Map.get()和相關方法*/
final Node<K,V> getNode(int hash, Object key) {Node<K,V>[] tab; Node<K,V> first, e; int n; K k;// 1. 檢查table是否初始化,長度是否大于0,以及根據hash計算出的位置上是否有節點if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) {// 2. 檢查第一個節點,如果匹配,直接返回if (first.hash == hash && // always check first node((k = first.key) == key || (key != null && key.equals(k))))return first;// 3. 如果第一個節點不匹配,且存在后續節點if ((e = first.next) != null) {// 3.1 如果是紅黑樹,調用getTreeNode在樹中查找if (first instanceof TreeNode)return ((TreeNode<K,V>)first).getTreeNode(hash, key);// 3.2 如果是鏈表,遍歷鏈表查找do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}// 4. 如果table為空,或桶為空,或遍歷完沒找到,則返回nullreturn null;
}

參考鏈接:https://xiaolincoding.com/interview/collections.html#hashmap%E7%9A%84put%E8%BF%87%E7%A8%8B%E4%BB%8B%E7%BB%8D%E4%B8%80%E4%B8%8B

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/88245.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/88245.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/88245.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

雙立柱式帶鋸床cad【1張總圖】+設計說明書+絳重

雙立柱式帶鋸床 摘 要 隨著機械制造技術的進步&#xff0c;制造業對于切割設備的精度、效率和穩定性要求越來越高。雙立柱式帶鋸床作為一種重要的切割設備&#xff0c;必須能夠滿足工業生產對于高精度、高效率的需求。 雙立柱式帶鋸床是一種重要的工業切割設備&#xff0c;其結…

在線JS解密加密配合ECC保護

在線JS解密加密配合ECC保護 1. ECC加密簡介 定義 ECC&#xff08;Elliptic Curve Cryptography&#xff09;是一種基于橢圓曲線數學的公鑰加密技術&#xff0c;利用橢圓曲線離散對數問題&#xff08;ECDLP&#xff09;實現高安全性。 背景 1985年&#xff1a;Koblitz&#xff0…

使用 Docker Compose 簡化 INFINI Console 與 Easysearch 環境搭建

前言回顧 在上一篇文章《搭建持久化的 INFINI Console 與 Easysearch 容器環境》中&#xff0c;我們詳細介紹了如何使用基礎的 docker run 命令&#xff0c;手動啟動和配置 INFINI Console (1.29.6) 和 INFINI Easysearch (1.13.0) 容器&#xff0c;并實現了關鍵數據的持久化&…

Word 怎么讓段落對齊,行與行之間寬一點?

我們來分兩步解決&#xff1a;段落對齊 和 調整行距。 這兩個功能都集中在Word頂部的【開始】選項卡里的【段落】區域。 第一步&#xff1a;讓段落對齊 “對齊”指的是段落的左右邊緣如何排列。通常有四種方式。 操作方法&#xff1a;將鼠標光標點在你想修改的那個段落里的任意…

Attention機制完全解析:從原理到ChatGPT實戰

一、Attention的本質與計算步驟 1.1 核心思想 動態聚焦&#xff1a;Attention是一種信息分配機制&#xff0c;讓模型在處理輸入時動態關注最重要的部分。類比&#xff1a;像人類閱讀時用熒光筆標記關鍵句子。 1.2 計算三步曲&#xff08;以"吃蘋果"為例&#xff09; …

2025年3月青少年電子學會等級考試 中小學生python編程等級考試三級真題答案解析(判斷題)

博主推薦 所有考級比賽學習相關資料合集【推薦收藏】1、Python比賽 信息素養大賽Python編程挑戰賽 藍橋杯python選拔賽真題詳解

HTML5 新特性詳解:從語義化到多媒體的全面升級

很多小伙伴本都好奇&#xff1a;HTML5有什么功能是以前的HTML沒有的&#xff1f; 今天就給大家說道說道 HTML5 作為 HTML 語言的新一代標準&#xff0c;帶來了諸多革命性的新特性。這些特性不僅簡化了前端開發流程&#xff0c;還大幅提升了網頁的用戶體驗和功能性。本文將深入…

mac安裝docker

1、下載docker-desktop https://www.docker.com/products/docker-desktop/2、安裝&#xff0c;雙擊安裝 3、優化docker配置 默認配置 cat ~/Library/Group\ Containers/group.com.docker/settings-store.json {"AutoStart": false,"DockerAppLaunchPath": …

mapbox進階,繪制不隨地圖旋轉的矩形,保證矩形長寬沿屏幕xy坐標方位

????? 主頁: gis分享者 ????? 感謝各位大佬 點贊?? 收藏? 留言?? 加關注?! ????? 收錄于專欄:mapbox 從入門到精通 文章目錄 一、??前言1.1 ??mapboxgl.Map 地圖對象1.2 ??mapboxgl.Map style屬性1.3 ??line線圖層樣式1.4 ??circle點圖層樣…

${project.basedir}延申出來的Maven內置的一些常用屬性

如&#xff1a;${project.basedir} 是 Maven 的內置屬性&#xff0c;可以被 pom.xml 直接識別。它表示當前項目的根目錄&#xff08;即包含 pom.xml 文件的目錄&#xff09;。 Maven 內置的一些常用屬性&#xff1a; 項目相關&#xff1a; ${project.basedir} <!-- 項…

[特殊字符] Python 批量生成詞云:讀取詞頻 Excel + 自定義背景 + Excel to.png 流程解析

本文展示如何用 Python 從之前生成的詞頻 Excel 文件中讀取詞頻數據&#xff0c;結合 wordcloud 和背景圖&#xff0c;批量生成直觀美觀的詞云圖。適用于文本分析、內容展示、報告可視化等場景。 &#x1f4c2; 第一步&#xff1a;讀取所有 Excel 詞頻文件 import os from ope…

模擬網絡請求的C++類設計與實現

在C開發中&#xff0c;理解和模擬網絡請求是學習客戶端-服務器通信的重要一步。本文將詳細介紹一個模擬HTTP網絡請求的C類庫設計&#xff0c;幫助開發者在不涉及實際網絡編程的情況下&#xff0c;理解網絡請求的核心概念和工作流程。 整體架構設計 這個模擬網絡請求的類庫主要由…

移動機器人的認知進化:Deepoc大模型重構尋跡本質

統光電尋跡技術已逼近物理極限。當TCRT5000傳感器在強烈環境光下失效率超過37%&#xff0c;當PID控制器在路徑交叉口產生63%的決策崩潰&#xff0c;工業界逐漸意識到&#xff1a;導引線束縛的不僅是車輪&#xff0c;更是機器智能的演化可能性。 ?技術破局點出現在具身認知架構…

記錄一次pip安裝錯誤OSError: [WinError 32]的解決過程

因為要使用 PaddleOCR&#xff0c;需要安裝依賴。先通過 conda新建了虛擬環境&#xff0c;然后安裝 PaddlePaddle&#xff0c;繼續安裝 PaddleOCR&#xff0c;上述過程我是在 VSCode的終端中處理&#xff0c;結果報錯如下&#xff1a;Downloading multidict-6.6.3-cp312-cp312-…

后端id設置long類型時,傳到前端,超過19位最后兩位為00

文章目錄一、前言二、問題描述2.1、問題背景2.2、問題示例三、解決方法3.1、將ID轉換為字符串3.2、使用JsonSerialize注解3.3、使用JsonFormat注解一、前言 在后端開發中&#xff0c;我們經常會遇到需要將ID作為標識符傳遞給前端的情況。當ID為long類型時&#xff0c;如果該ID…

SpringAI學習筆記-MCP客戶端簡單示例

MCP客戶端是AI與外部世界交互的橋梁。在AI系統中&#xff0c;大模型雖然具備強大的認知能力&#xff0c;卻常常受限于數據孤島問題&#xff0c;無法直接訪問外部工具和數據源。MCP協議應運而生&#xff0c;作為標準化接口解決這一核心挑戰。該協議采用客戶端-服務端架構&#x…

postgresql|數據庫|系統性能監控視圖pg_stat與postgresql數據庫的調優(備忘)

一、 寫作初衷 通常,我們使用navicat這樣的數據庫圖形管理工具,只能看到用戶層面的表,視圖,而系統層面的表,視圖,函數是無法看到的,這些表,視圖和函數好像也可以稱之為內模式;而這些視圖,函數的作用是非常大的,其中pg_stat 族系統視圖可以得到數據庫的詳細運行信息…

網絡安全護網實戰:攻擊手段解析與防御策略

在網絡安全領域&#xff0c;護網行動中對各類攻擊方式和漏洞原理的掌握至關重要。本文將詳細解析常見的攻擊方式及其背后的漏洞原理&#xff0c;幫助大家提升護網技能。一、常見攻擊方式及漏洞原理1. SQL注入漏洞? 定義&#xff1a;將惡意的數據庫語句注入到后臺數據庫去執行&…

使用alist+RaiDrive+webdav將百度夸克網盤變為本地電腦磁盤方法教程

由于每天都要操作網盤不下十幾次&#xff0c;頻繁啟動網盤比較麻煩。 使用百度夸克網盤的webdav服務可以將百度夸克網盤掛載到本地電腦上&#xff0c;就像操作本地電腦硬盤一樣操作網盤&#xff0c;非常方便。我們以alistraidrive為例演示。 首先打開百度網盤pan.baidu.com&a…

C# 入門學習教程(二)

文章目錄一、操作符詳解1、操作符概覽2、操作符的本質3、操作符的優先級4、同級操作符的運算順序5、 各類操作符的示例二、表達式&#xff0c;語句詳解1. 表達式的定義2. 各類表達式概覽3. 語句的定義4. 語句詳解一、操作符詳解 C# 中的操作符是用于執行程序代碼運算的符號&am…