go語言map底層及擴容機制原理詳解(上)

底層數據結構-哈希表

go語言map的底層數據結構是哈希表:通過哈希表來存儲鍵值對,通過hash函數把鍵值對散列到一個個桶(bucket)中。

什么是哈希表?

  • 在順序結構以及平衡樹中,元素與其的存儲位置之間沒有對應關系,因此查找一個元素時,必須進行多次比較。所以順序查找的時間復雜度為O(N),而平衡樹中則為樹的高度O(log2N)。
  • 為了減少搜素時元素比較的次數,是否有一種方法可以不經過任何比較,通過元素的存儲位置與它的關鍵碼以O(1)的時間復雜度直接找到該元素呢?
  • 哈希表就是通過某種函數(hash)來使元素的存儲位置和其元素值之間建立一一映射的關系,那么就可以通過這種關系快速找到該元素。(數組就是一種簡單的哈希表)

如何處理哈希沖突?

  • 當兩個或多個健具有相同的哈希值,即為出現了哈希沖突,它們會被存放在同一個桶中。go采用拉鏈法來解決哈希沖突的問題,即在同一個桶內部通過鏈接(鏈表)存儲所有沖突的鍵值對。
  • 不過拉鏈法在當哈希沖突出現的次數相當頻繁時,會將常數級的時間復雜度上升甚至到線性級。加載因子的出現就是為了避免過多的哈希沖突導致哈希表的退化。

無序性

  • 由于go語言的map是通過哈希表來實現的,由于哈希函數的特性,是無法依據一定的順序來存儲的。因此go的map是無序的。

map的擴容機制

在哈希表中,當元素達到一定的數量(超過加載因子設定的比例),為了保持操作的效率,需要對哈希表進行擴容。擴容通常需要創建一個更大的哈希表,并將現有元素重新映射到新表中。

底層實現


type hmap struct {count     int    // 元素的個數B         uint8  // buckets 數組的長度就是 2^B 個overflow uint16 // 溢出桶的數量buckets    unsafe.Pointer // 2^B個桶對應的數組指針oldbuckets unsafe.Pointer  // 發生擴容時,記錄擴容前的buckets數組指針extra *mapextra //用于保存溢出桶的地址
}type mapextra struct {overflow    *[]*bmapoldoverflow *[]*bmapnextOverflow *bmap
}type bmap struct {tophash [bucketCnt]uint8
}//在編譯期間會產生新的結構體
type bmap struct {tophash [8]uint8 //存儲哈希值的高8位data    byte[1]  //key value數據:key/key/key/.../value/value/value...overflow *bmap   //溢出bucket的地址
}

在go的map實現中,它的底層結構體是hmap,hmap里維護著若干個bucket數組 (即桶數組)。每個桶中保存了8個鍵值對,如果8個滿了,又來了一個kv到了這個桶中,會使用overflow連接下一個桶,即桶溢出。

  • 對于哈希沖突:當兩個不同的key落到了同一個桶中就是發生了哈希沖突,則會采用拉鏈法,從前往后找一個空位進行插入。如果桶滿了,當前桶就會連接到下一個溢出桶。

擴容基本步驟

  1. 觸發擴容:
    • 當向map中添加新元素時,如果元素數量超過了當前哈希表容量和加載因子的乘積,就會觸發擴容。加載因子是一個決定性能與內存使用之間的閾值,防止哈希表的退化。
  2. 分配新表
    • go在運行是會創建一個新的哈希表,其容量為原來的兩倍。這樣做可以減少再次擴容的可能,并提供足夠的空間來避免過多的哈希沖突。
  3. 數據遷移
    • 將舊哈希表中的現有元素遷移到新表中。每個元素的哈希中將根據新表的大小容量重新計算,來確定它們在新表的位置。
    • map非常大的情況下,每次遷移所有的元素,會出現長時間的暫停。在go1.8版本之后,這個步驟是漸進式的:每次向map`添加新元素或查找時,都會遷移一小部分元素,避免長時間的暫停。
  4. 更新引用
    • 當所有元素都遷移到新的哈希表中后,原來的哈希表將會被丟棄,map的內部引用將指向新表。

總結

  1. 要提供合適的初始容量。
    由于每次擴容時,需要重新計算所有元素的哈希值并將它們分配到新的桶中,這是一個相當花時間的操作。因此,如果我們事先知道map大約會存儲多少數據,可以實現在創建map時通過提供合適的初始容量來減少擴容次數,從而提高map的性能:
    myMap := make(map[string]int, initialCapacity)

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

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

相關文章

SwiftUI中的@StateObject和@ObservedObject的區別

SwiftUI中的StateObject和ObservedObject屬性包裝器指示視圖更新以響應被觀察對象的變化。雖然這兩個屬性包裝器看起來很相似,但在使用SwiftUI構建應用程序時,有一個關鍵的區別需要理解。 兩個屬性包裝器都要求對象符合ObservableObject協議。這個協議表…

表征和基于結構的蛋白質工程:黃芪特異性皂苷乙酰轉移酶-文獻精讀14

Characterization and structure-based protein engineering of a regiospecific saponin acetyltransferase from Astragalus membranaceus 表征和基于結構的蛋白質工程:黃芪特異性皂苷乙酰轉移酶,一篇乙酰基轉移酶文章精讀分享~ 摘要 乙酰化有助于許…

【C++】繼承相關(基類與派生類的繼承關系以及細節整理)

目錄 00.引言 01.繼承的定義 02.基類和派生類對象 03.繼承中的作用域 04.派生類的默認成員函數 05.友元、靜態成員 00.引言 繼承是面向對象編程中的一個重要概念,它的作用是創建一個新的類,該類可以從一個已存在的類(父類/基類&#x…

服務攻防——數據庫安全

第一步: 端口掃描:nmap 掃不到端口:端口被修改,防護軟件,放在內網環境 mysql 內置端口3306 第一種官方漏洞 第一步:先掃描有什么端口開發 用這個錯誤密碼一直訪問,最終就進去了 弱口令猜解 不可以直接猜解&#x…

WEB后端復習——MVC、SSM【含登錄頁面代碼】

MVC(Model-View-Controller)是一種軟件設計模式,用于將應用程序分解為三個相互關聯的組件:模型(Model)、視圖(View)和控制器(Controller)。這種模式在構建用戶…

機器人學導論實驗1—CoppeliaSim 平臺介紹及初步使用BJTU

1. 實驗內容分析 對實驗內容的理解及關鍵點: 理解這個實驗的關鍵點在于理解如何使用CoppeliaSim和MATLAB來控制和操作機器人。需要熟悉這兩個工具的基本操作,例如如何加載場景、如何修改機器人參數、如何使用MATLAB客戶端程序來控制機器人等。此外&#…

Docker 部署 Prometheus 實現一個極簡的 QPS 監控

背景 : Prometheus 是近年來最流行的開源監控框架, 其功能強大且易于使用, 擁有各種主流后端語言(Java/Go/Python/Node.js等)與各種場景(如web handler/ k8s/Nginx/MySQL等)的客戶端, 并自帶圖形化顯示頁面。分享一個快速入門Prometheus 的教程, 實現一個極簡的, 后端開發需要特…

Nginx-基礎-基礎配置-Location

Location 參數匹配模式 參數匹配方式匹配模式說明注意事項精準匹配普通字符串匹配用于標準uri前,要求請求字符串與uri精準匹配,成功則立即處理,nginx停止搜索其他匹配。~正則匹配正則表達式匹配用于正則uri,表示uri包含正則表達…

使用 Docker 輕松部署 Spring Boot 應用

當今軟件開發領域,Docker 和 Spring Boot 的組合已成為開發和部署應用程序的黃金標準。在這篇博客中,我們將詳細探討如何將 Spring Boot 應用容器化并使用 Docker 進行部署,確保你的部署過程既高效又可靠。 引言 Docker 提供了一個標準化的…

基于SSM的理發店會員管理系統的設計和實現(有報告)。Javaee項目。ssm項目。

演示視頻: 基于SSM的理發店會員管理系統的設計和實現(有報告)。Javaee項目。ssm項目。 項目介紹: 采用M(model)V(view)C(controller)三層體系結構&#xff0…

Docker安裝達夢數據庫

1.確保已安裝Docker 可參考:Linux安裝Docker-CSDN博客 2.上傳dm鏡像并導入安裝包 可以從:產品下載 | 達夢數據庫下載dm鏡像,如下圖: docker load -i dm8_20230808.tar 3.導入后查看鏡像 docker images 4.啟動容器 docker run …

圖的概念、性質和存儲與簡單遍歷

前置知識:樹的基本概念及性質 為了保證學習效果,請保證已經掌握前置知識之后,再來學習本章節!如果在閱讀中遇到困難,也可以回到前面章節查閱。 學習目標 掌握圖的基本概念掌握圖的一些性質 圖的概念 基本概念 圖 (…

Pytorch如何計算網絡參數

方法一. 利用pytorch自身 PyTorch是一個流行的深度學習框架,它允許研究人員和開發者快速構建和訓練神經網絡。計算一個PyTorch網絡的參數量通常涉及兩個步驟:確定網絡中每個層的參數數量,并將它們加起來得到總數。 以下是在PyTorch中計算網…

如何在 CloudFlare 里屏蔽/攔截某個 IP 或者 IP 地址段

最近除了接的 CloudFlare 代配置訂單基本很少折騰自己的 CloudFlare 配置了,今天給大家簡單的講解一下如何在 CloudFlare 里屏蔽/攔截 IP 地址和 IP 地址段,雖然明月一直都很反感針對 IP 的屏蔽攔截,但不得不說有時候還是很有必要的。并且,既然可以攔截屏蔽 IP 自然也可以但…

鴻蒙內核源碼分析(VFS篇) | 文件系統和諧共處的基礎

基本概念 | 官方定義 VFS(Virtual File System)是文件系統的虛擬層,它不是一個實際的文件系統,而是一個異構文件系統之上的軟件粘合層,為用戶提供統一的類Unix文件操作接口。由于不同類型的文件系統接口不統一&#x…

Flink HA模式下JobManager切換時發送告警

資源&版本信息 Flink版本1.14.6 運行平臺:K8s HA使用ZK(使用K8s的ETC應該是一個道理) 詳解Flink HA原理 Flink啟動時會創建HighAvailabilityServices提供HA和相關基礎服務,其中包括leaderRetrievalService和LeaderElecti…

搜索引擎的設計與實現(二)

目錄 3 搜索引擎的基本原理 3.1搜索引擎的基本組成及其功能 l.搜索器 (Crawler) 2.索引器(Indexer) 3.檢索器(Searcher) 4.用戶接口(UserInterface) 3.2搜索引擎的詳細工作流程 4 系統分析與設計 4.1系統分析 4.2系統概要設計 4.2系統實現目標 前面內容請移步 搜索引…

Rust 語言不支持 goto 語句

一、Rust 不提供 goto 語句 Rust 語言并沒有提供 goto 語句。goto 語句在很多現代編程語言中已經不再被推薦使用,因為它可能導致代碼的流程變得難以跟蹤和理解,特別是在復雜的程序中。Rust 語言設計者選擇了更加結構化和可預測的控制流語句,…

關于C++多態的復習總結

多態 簡介: 面向對象的三大特性之一,多態顧名思義即具有多種形態,即去執行某個行為時,當不同的對象去執行時會產生不同的狀態 構成多態的條件 條件一 必須通過基類(父類)的指針或者引用調用虛函數(函數…

寧夏銀川市起名專家的老師顏廷利:死神(死亡)并不可怕,可怕的是...

在中國優秀傳統文化之中,漢語‘巳’字與‘四’同音,在阿拉伯數字里面,通常用‘4’來表示; 湖南長沙、四川成都、重慶、寧夏銀川最靠譜最厲害的起名大師的老師顏廷利教授指出,作為漢語‘九’字,倘若是換一個…