深入理解 HashMap 的索引計算:右移與異或的作用

在 Java 中,HashMap 是一種高效的數據結構,它通過將鍵映射到數組中的索引位置來實現快速的插入和查找。但之前看源碼總是理解到它要hash之后散列到數組中某一個位置,但卻從未深究它究竟怎么散列的,如果不夠散那就意味著hash沖突增加,在這個過程中,散列函數的設計至關重要,特別是右移和異或操作如何幫助減少哈希沖突。

  • 沖突對性能的影響
    • 盡管 HashMap 使用鏈表和紅黑樹來處理沖突,但沖突仍然會對性能產生顯著影響:
    • 查詢性能下降:當多個元素存儲在同一個桶中時,查找這些元素的時間復雜度可能退化到 O(n),而不是理想情況下的 O(1)。
    • 插入性能下降:插入新元素時,如果發生沖突,可能需要遍歷鏈表或調整紅黑樹,導致插入時間復雜度增加。
    • 內存使用效率:沖突頻繁發生時,某些桶可能存儲過多元素,而其他桶則幾乎沒有元素,這種不均勻性可能導致內存浪費。
    • 如果散列性不足,導致大量沖突,HashMap的性能將受到嚴重影響。在最壞的情況下,所有元素都可能映射到同一個桶,形成一個鏈表或紅黑樹,導致查找、插入和刪除操作的時間復雜度退化到 O(n)。

1. HashMap 的基本概念

HashMap 使用一個數組來存儲鍵值對(key-value pairs),每個鍵通過其哈希值計算出一個數組索引。理想情況下,不同的鍵應該映射到不同的索引,以避免哈希沖突。
數組:用于存儲桶(bucket),每個桶可以存儲多個鍵值對。
鏈表或紅黑樹:用于處理哈希沖突,桶中的每個元素可以是鏈表或紅黑樹。

1.1 數據結構

HashMap 的核心數據結構如下:

static class Node<K,V> {final int hash; // 哈希值final K key;    // 鍵V value;        // 值Node<K,V> next; // 下一個節點
}

2. 計算索引位置的流程

2.1 獲取哈希值

當調用 put(key, value)get(key) 方法時,HashMap 首先會通過鍵的 hashCode() 方法獲取哈希值。以下是 put 方法的簡化實現:

public V put(K key, V value) {int hash = (key == null) ? 0 : hash(key);// 計算索引位置int index = indexFor(hash, table.length);// 進行插入操作
}

2.2 散列函數的實現

HashMap 中的散列函數是通過 hash 方法實現的。該方法的實現如下:

static final int hash(int h) {h ^= (h >>> 16); // 右移 16 位并異或return h ^ (h >>> 16); // 再次混合
}

2.3 計算索引位置

HashMap 中,索引位置的計算是通過以下方式實現的:

int index = (n - 1) & hash;

這里,n 是數組的長度,hash 是經過處理的哈希值。

3. 右移與異或的作用

3.1 為什么需要右移與異或?

在進行哈希值的計算時,HashMap 使用右移和異或的組合來增強哈希值的隨機性。讓我們通過一個示例來詳細分析這一過程。

3.2 示例分析

假設我們有一個鍵 "key1",其哈希值為 0xB2690FF0。我們將分析右移和異或如何影響哈希值的混合。

原始哈希值
hash:  10110010 01101001 00001111 11110000  (二進制)

3.3 右移操作

首先,我們進行右移 16 位的操作:

hash >>> 16:
原始: 10110010 01101001 00001111 11110000
結果: 00000000 00000000 10110010 01101001

3.4 異或運算

接下來,我們進行異或運算:

new_hash = hash ^ (hash >>> 16);
計算過程
原始哈希值:  10110010 01101001 00001111 11110000
右移結果:    00000000 00000000 10110010 01101001
-----------------------------------------------
new_hash:     10110010 01101001 10110101 10011001

3.5 高位信息的參與

在計算索引時,HashMap 使用以下公式:

if ((p = tab[i = (n - 1) & hash]) == null) {// 插入新節點
}

這里,(n - 1) 是數組長度減去 1 的值。由于數組長度通常是 2 的冪(例如 16、32、64 等),n - 1 的二進制表示通常以 0 結尾,導致在進行按位與運算時,只有低位信息參與計算。

3.6 按位與的影響

因為按位與運算只有在對應的位都為 1 時結果才為 1,若 hash 的高位信息沒有參與,可能導致多個不同的哈希值映射到同一個索引位置,從而增加哈希沖突的概率。例如:

  • 假設 n = 16,即 n - 1 = 15,其二進制為 0000 1111
  • 如果 hash 的高位為 0,低位的變化可能導致多個不同的 hash 值在與 15 進行與運算后得到相同的索引。

3.7 通過右移與異或減少沖突

通過右移和異或,HashMap 能夠打破哈希值的模式,使得高位信息參與到最終的哈希計算中。這種方式使得即使在低位相同的情況下,高位的不同也能影響最終的索引計算,從而有效減少哈希沖突的發生。

4. 減少哈希沖突的原理

4.1 高位與低位的混合

通過右移和異或,HashMap 有效地將高位信息引入低位,增強了哈希值的隨機性,使得相似的鍵在哈希表中更可能映射到不同的索引位置。

4.2 均勻分布

理想的散列函數應該能夠均勻地分布哈希值。如果哈希值的分布不均勻,某些桶可能會存儲過多的元素,從而導致性能下降。通過混合高位和低位,HashMap 提高了哈希值的隨機性,使得鍵值對能夠更加均勻地分布在桶中。

4.3 沖突處理機制

盡管 HashMap 通過上述方法減少了哈希沖突,但仍然可能發生沖突。當多個鍵映射到同一個索引時,HashMap 使用以下兩種方法來處理沖突:

a. 鏈表法

在同一個桶中使用鏈表存儲所有具有相同索引的鍵值對。當發生沖突時,新元素會被添加到鏈表的末尾。

b. 紅黑樹

當某個桶中的元素數量超過一定閾值(如 8),HashMap 會將鏈表轉換為紅黑樹,以提高查找效率。紅黑樹的查找時間復雜度為 O(log n),比鏈表的 O(n) 更高效。

5. 總結

通過對 HashMap 源碼的分析,我們可以看到它是如何計算索引位置的,以及散列函數在其中的關鍵作用。右移和異或的組合使用有效地混合了哈希值的高位和低位,從而減少了哈希沖突的概率,盡可能的確保了 HashMap 的高效性能。如果對你有幫助歡迎點贊支持。

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

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

相關文章

overleaf較高級的細節指令

換行命令 原來代碼是將三個矩陣表達式在同一行顯示&#xff0c;使用aligned環境&#xff08;需引入amsmath宏包&#xff0c;一般文檔導言區默認會引入&#xff09;&#xff0c;把三個矩陣的定義分別放在不同行&#xff0c;可通過\\換行。 對齊命令 &放在等號前&#xff0…

LiteLLM:統一API接口,讓多種LLM模型調用如臂使指

在人工智能迅猛發展的今天,各種大語言模型(LLM)層出不窮。對開發者而言,如何高效集成和管理這些模型成為一個棘手問題。LiteLLM應運而生,它提供了一個統一的API接口,讓開發者可以輕松調用包括OpenAI、Anthropic、Cohere等在內的多種LLM模型。本文將深入介紹LiteLLM的特性、…

Google語法整理

以下是從整理出的 Google 語法&#xff1a; site&#xff1a;指定域名&#xff0c;如 “apache site:bbs.xuegod.cn”&#xff0c;可查詢網站的收錄情況 。 inurl&#xff1a;限定在 url 中搜索&#xff0c;如 “inurl:qq.txt”&#xff0c;可搜索 url 中包含特定內容的頁面&a…

python 寫一個工作 簡單 番茄鐘

1、圖 2、需求 番茄鐘&#xff08;Pomodoro Technique&#xff09;是一種時間管理方法&#xff0c;由弗朗西斯科西里洛&#xff08;Francesco Cirillo&#xff09;在 20 世紀 80 年代創立。“Pomodoro”在意大利語中意為“番茄”&#xff0c;這個名字來源于西里洛最初使用的一個…

Compose Multiplatform iOS 穩定版發布:可用于生產環境,并支持 hotload

隨著 Compose Multiplatform 1.8.0 的發布&#xff0c;iOS 版本也引來的第一個穩定版本&#xff0c;按照官方的原話&#xff1a;「iOS Is Stable and Production-Ready」 &#xff0c;而 1.8.0 版本&#xff0c;也讓 Kotlin 和 Compose 在移動端有了完整的支持。 在 2023 年 4 …

Jenkins 服務器上安裝 Git

安裝 Git # 更新包列表 sudo apt update# 安裝 Git sudo apt install git 驗證安裝 # 檢查 Git 版本 git --version 查看所有全局配置 git config --global --list 查看特定配置項 # 查看用戶名配置 git config --global user.name# 查看郵箱配置 git config --global u…

OpenHarmony SystemUI開發——實現全局導航欄和狀態欄關閉

在實際生產中&#xff0c;進場遇到需要關閉導航欄和狀態欄的需求&#xff0c;現分享解決辦法&#xff1a; 開發環境 OpenHarmony 5.0.0r 代碼分析 思路&#xff1a; launcher本身可以關閉 導航欄&#xff08;實際是 公共事件&#xff0c;發送消息給systemUI來實控制&#x…

大模型微調終極方案:LoRA、QLoRA原理詳解與LLaMA-Factory、Xtuner實戰對比

文章目錄 一、微調概述1.1 微調步驟1.2 微調場景 二、微調方法2.1 三種方法2.2 方法對比2.3 關鍵結論 三、微調技術3.1 微調依據3.2 LoRA3.2.1 原理3.2.2 示例 3.3 QLoRA3.4 適用場景 四、微調框架4.1 LLaMA-Factory4.2 Xtuner4.3 對比 一、微調概述 微調&#xff08;Fine-tun…

單片機-STM32部分:10-2、邏輯分析儀

飛書文檔https://x509p6c8to.feishu.cn/wiki/VrdkwVzOnifH8xktu3Bcuc4Enie 安裝包如下&#xff1a;根據自己的系統選擇&#xff0c;目前這個工具只有window版本哦 安裝方法比較簡單&#xff0c;都按默認下一步即可&#xff0c;注意不要安裝到中文路徑哦。 其余部分參考飛書文檔…

uniapp-商城-48-后臺 分類數據添加修改彈窗bug

在第47章的操作中&#xff0c;涉及到分類的添加、刪除和更新功能&#xff0c;但發現uni-popup組件存在bug。該組件的函數接口錯誤導致在小程序中出現以下問題&#xff1a;1. 點擊修改肉類名稱時&#xff0c;回調顯示為空&#xff0c;并報錯“setVal is not defined”&#xff0…

STM32-ADC模數轉換器(7)

目錄 一、ADC簡介 二、逐次逼近型ADC 三、ADC基本結構圖 四、規則組的四種轉換模式 五、轉換時間 對GPIO來說&#xff0c;它只能讀取引腳的高低電平&#xff0c;使用了ADC模數轉化器之后&#xff0c;就可以對高電平和低電平之間的任意電壓進行量化&#xff0c;最終用一個變…

智能商品推薦系統技術路線圖

智能商品推薦系統技術路線圖 系統架構圖 --------------------------------------------------------------------------------------------------------------- | 用戶交互層 (Presentation Layer) …

【Docker系列】docker inspect查看容器部署位置

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…

標量/向量/矩陣/張量/范數詳解及其在機器學習中的應用

標量&#xff08;Scalar&#xff09;、向量&#xff08;Vector&#xff09;、矩陣&#xff08;Matrix&#xff09;、張量&#xff08;Tensor&#xff09;與范數&#xff08;Norm&#xff09;詳解及其在機器學習中的應用 1. 標量&#xff08;Scalar&#xff09; 定義&#xff1…

【2025年】基于電腦的jdk1.8通過idea創建springboot2.x版本(非常簡潔快速)

【2025年】基于電腦的jdk1.8通過idea創建springboot2.x版本 提示&#xff1a;幫幫志會陸續更新非常多的IT技術知識&#xff0c;希望分享的內容對您有用。本章分享的是springboot的使用。前后每一小節的內容是存在的有&#xff1a;學習and理解的關聯性。【幫幫志系列文章】&…

SierraNet協議分析使用指導[RDMA]| 如何設置 NVMe QP 端口以進行正確解碼

在解碼RoCEv2數據包&#xff08;包括TCP RDMA和RoCE RDMA&#xff09;時&#xff0c;若捕獲的跟蹤數據無法正確解碼&#xff0c;通常需要執行特定的解碼步驟。對于RoCE RDMA跟蹤數據的處理&#xff0c;分析器主要采用兩種方式獲取必要信息以實現數據包解碼&#xff1a; 首先&am…

JavaScript基礎-局部作用域

在JavaScript中&#xff0c;理解不同種類的作用域是掌握這門語言的關鍵之一。作用域決定了變量和函數的可訪問性&#xff08;即可見性和生命周期&#xff09;。與全局作用域相對應的是局部作用域&#xff0c;它限制了變量和函數只能在其定義的特定范圍內被訪問。本文將深入探討…

李沐動手深度學習(pycharm中運行筆記)——09.softmax回歸+圖像分類數據集+從零實現+簡潔實現

09.softmax回歸圖像分類數據集從零實現簡潔實現&#xff08;與課程對應&#xff09; 目錄 一、softmax回歸 1、回歸 vs 分類 2、經典分類數據集&#xff1a; 3、從回歸到分類——均方損失 4、從回歸到多類分類——無校驗比例 5、從回歸到多類分類——校驗比例 6、softmax和…

C++八股——內存分配

文章目錄 1. 虛擬內存空間2. malloc和free3. new和delete4. 內存池 1. 虛擬內存空間 程序進程的虛擬內存空間是操作系統為每個進程提供的獨立、連續的邏輯地址空間&#xff0c;與物理內存解耦。其核心目的是隔離進程、簡化內存管理&#xff0c;并提供靈活的內存訪問控制。 &am…

【Linux基礎】網絡相關命令

目錄 netstat命令 1.1 命令介紹 1.2 命令格式 1.3 常用選項 1.4 常用命令實例 1.4.1 顯示所有TCP連接 1.4.2 查看路由表 1.4.3 實時監控網絡接口流量 1.4.4 查看監聽中的端口以及關聯進程 ping命令 2.1 命令介紹 2.2 命令格式 2.3 常用選項 2.4 常用示例 ifconfi…