go-map系统学习

map底层结构

Goland的map的底层结构使用hash实现,一个hash表里有多个hash表节点,即bucket,每个bucket保存了map中的一个或者一组键值对。

map结构定义:

runtime/map.go:hmap
type hmap struct {// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.// Make sure this stays in sync with the compiler's definition.count     int // # live cells == size of map.  Must be first (used by len() builtin)flags     uint8B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for detailshash0     uint32 // hash seedbuckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growingnevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)extra *mapextra // optional fields
}

其中 buckets为 bucket数组指针,数组的大小为2^B

若B=2则,buckets数量是2^2=4

imghash结果示意图

bucket是桶,通常用来表示一类数据的集合。hash表里的bucket称之为哈希桶,通常是求余后结果相同的落入一个桶中。

根据key值计算出哈希值,取hash值的低8位与2^hmap.B取模确定存放数据到哪个bucket

bucket结构

bucket结构定义,逻辑上是如下的结果:

type bmap struct {tophash [8]uint8 //存储哈希值的高8位data    byte[1]  //key value数据:key/key/key/.../value/value/value...overflow *bmap   //溢出bucket的地址
}
  • tophash 是长度为8的数组,哈希值高位相同的分到同一个bucket中的键,存入当前bucket时候,会将哈希值的高位存在在该数组中,以方便后续匹配。

  • data这块其实是8个key、8个value,但是我们不能直接看到;为了优化对齐,go采用了key放在一起,value放在一起的存储方式。

  • overflow表示hash冲突发生时,下一个溢出桶的地址

上述中data和overflow并不是在结构体中显示定义的,实际包代码中定义也看不到,但是有这个逻辑存在。

实际源码定义可参考:https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/runtime/map.go

这样的定义实现了:若某个哈希对应的bucket空间已满,则需要创建一个新的bmap存储键值对,无数个bmap通过overflow指针进行关联。buckets链条是类似于这样的结构:

bucket链条结构

hmap和bmap结构

结合以上hmap和bmap的结构,我们可以总结一个map基本结构:

img

负载因子

负载因子用来衡量一个哈希表冲突情况,公式为:

负载因子=键的数量/bucket数量

对于上图的负载因子:6/3=2

哈希表需要将负载因子控制在合适的大小,超过阈值需要进行rehash,也即键值对重新组织:

  • 哈希因子过小,说明空间利用率低
  • 哈希因子过大,说明冲突严重,存取效率低

Go语言负载因子达到6.5时就会触发rehash

hash扩容

新元素添加时候,会检查是否需要扩容,扩容条件:

  1. 负载因子 > 6.5时,也即平均每个bucket存储的键值对达到6.5个。

  2. overflow数量 > 2^15时,也即overflow数量超过32768时。

增量式扩容

当负载因子过大,新建bucket,新的bucket长度是原2倍,旧bucket数据搬迁至新的bucket。搬迁过程采样逐步搬迁策略,即每次访问map都会触发一次搬迁,每次搬迁2个键值对。

渐进式扩容中,oldbuckets指向原来的桶。而buckets指向了新申请的bucket。新的键值对被插入新的bucket中。后续对map的访问操作会触发迁移,将oldbuckets中的键值对逐步的搬迁过来。当oldbuckets中的键值对全部搬迁完毕后,删除oldbuckets。

img

如上,bucket0已经有7个值,此时负载因子是7,超过6.5,当再存入一个key时,就会触发扩容。新建了bucket1,但是新建的bucket是buckets指向的,且在新的空间给bucket0留出位置,待一点点的将oldbuckets指向的空间数据迁移至新的bucket0空间,然后删除就得oldbuckets指向的空间。搬迁完成的空间示意图:

img

等量式扩容

所谓等量扩容,实际上并不是扩大容量。buckets数量不变,重新做一遍类似增量扩容的搬迁动作,把松散的键值对重新排列一次,以使bucket的使用率更高,进而保证更快的存取

map查找数据

  1. 首先根据key值计算出哈希值

  2. 取哈希值的低八位与2^hmap.B取模确定bucket位置

  3. 取高八位在桶数组中进行查找

  4. 如果tophash[i]中存储值也哈希值相等,则去找到该bucket中的key值进行比较

  5. 当前bucket没有找到,则继续从下个overflow的bucket中查找。

  6. 如果当前处于扩容搬迁过程,则优先从oldbuckets查找

map插入过程

  1. 根据key值算出哈希值
  2. 取哈希值低位与hmap.B取模确定bucket位置
  3. 查找该key是否已经存在,如果存在则直接更新值
  4. 如果没找到将key,将key插入

map操作

操作包括初始化、键的判断、遍历、作为函数参数使用

map声明与初始化

map是一种引用类型,有几种声明方法:

  1. 声明空的map
   var myMap map[string]int

这样声明的map是nil值,对它操作之前,必须使用make来初始化。

注意,struct结构中成员是map变量的话,在实际使用中必须再make初始化空间。

  1. 使用make直接声明+初始化
stringMap := make(map[string]string, n)

​ n表示长度,但是使用中是可以扩容的,相当于建议的长度。

  1. 使用字面量初始化
    myMap := map[string]int{  "one":   1,  "two":   2,  "three": 3,  }

:= 是一个特殊的赋值操作符,被称为“短变量声明”或“短变量赋值”。它主要用于在函数内部声明并初始化新的局部变量。使用 := 可以同时完成变量的声明和初始化,使得代码更加简洁。在全局作用域(即函数外部)不能使用 := 来声明变量,因为 := 会隐式地声明变量,而全局变量需要显式声明。

4,使用var关键字结合字面量来初始化

   var myMap = map[string]int{  "one":   1,  "two":   2,  "three": 3,  }

5,显示声明类型+初始化

   var myMap map[string]int = map[string]int{      "one":   1,      "two":   2,      "three": 3,   }

对于全局变量或需要在多个地方引用的变量,显式声明类型可能更加合适。

判断键是否存在

value, ok := sliceMap[key]

判断ok来判断值是否存在

  key := "小明"value, ok := deleMap[key]if ok {fmt.Println("key:", key, "value:", value)} else {fmt.Println("key:", key, "不存在")}

删除键值对

delete(mapName,key)

删除mapName map中的key值键值对

deleMap := make(map[string]int)deleMap["张三"] = 90deleMap["小明"] = 100deleMap["王五"] = 60fmt.Println("map:", deleMap)delete(deleMap, "小明")fmt.Println("map after delete:", deleMap)

delete是安全的操作,即使删除不存在的key也不报错。delete中如果查找删除失败将返回value类型对应的零值

	m1 := map[int]string{1: "sss", 2: "fff", 3: "zzz"}delete(m1, 5)for k, v := range m1 {fmt.Printf("%d ----> %s\n", k, v)}
//1 ----> sss
//2 ----> fff
//3 ----> zzz

遍历map

使用for range遍历map,第一个参数是key,第二是value

	for key, value := range scoreMap {fmt.Println(key, value)}

第二种遍历方法,只返回key,省略value

	for k := range scoreMap {fmt.Printf("%s----> %v\n", k, scoreMap[k])}

map长度

可以使用len函数获取map中键值对的个数:

 fmt.Println("len is:", len(stringMap))

map作为函数参数与返回值

map作为函数参数是引用传递

map作为函数返回值也是引用传递

	m1 := map[int]string{1: "sss", 2: "fff", 3: "zzz"}m2 := getAndChangeMap(m1)fmt.Println("map m2:", m2)fmt.Println("map m1:", m1)m2[4] = "yiyi"fmt.Println("change m2")fmt.Println("map m2:", m2)fmt.Println("map m1:", m1)
func getAndChangeMap(m map[int]string) map[int]string {m[23] = "lili"return m
}

打印内容

map m2: map[1:sss 2:fff 3:zzz 23:lili]
map m1: map[1:sss 2:fff 3:zzz 23:lili]
change m2
map m2: map[1:sss 2:fff 3:zzz 4:yiyi 23:lili]
map m1: map[1:sss 2:fff 3:zzz 4:yiyi 23:lili]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/425916.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开学C++:多态

引言:在我们去购买汽车票的时候,我们总会遇到成人全价,学生打折的情况。不同的对象(成人、学生)进行同一操作(购买车票),得到不同的结果(全价、打折)&#xf…

Redis实现发布/订阅功能(实战篇)

前言 博主在学习 Redis 实现发布订阅功能的时候,踩了太多的坑。 不是讲解不详细,看的一知半解;就是代码有问题,实际压根跑不起来! 于是博主萌生了自己写一个最新版且全程无错的博客供各位参考。希望各位不要把我才过…

深入理解IP地址分类及子网划分详解

在互联网时代,IP地址是网络通信的基础。无论是访问网站、发送电子邮件,还是进行数据传输,IP地址都扮演着至关重要的角色。本文将详细解析IP地址的分类及子网划分的原理,帮助你更好地理解网络架构及其应用。 一、什么是IP地址 IP…

教师薪酬管理系统的设计与实现

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理,然而,随着近些年信息技术的迅猛发展,让许多比较老套的信息管理模式进行了更新迭代,老师信息因为其管理内容繁杂,管理数量繁多导致手工进行处理不能满足广…

Android MediaPlayer + GLSurfaceView 播放视频

Android使用OpenGL 播放视频 概述TextureView的优缺点OpenGL的优缺点 实现复杂图形效果的场景参考 概述 在Android开发中,使用OpenGL ES来渲染视频是一种常见的需求,尤其是在需要实现自定义的视频播放界面或者视频特效时。结合MediaPlayer,我…

吸浮毛宠物空气净化器推荐,希喂、小米、有哈宠物空气净化器测评

养猫需谨慎,不然就要做猫奴一辈子啦!上次堂妹来我家住几天,刚开始还担心和猫处不来,不敢去摸它,走的时候已经约好下次来看它的时间,笑死我了。毕竟猫咪这么可爱,很少有人可以抵抗它的魅力。 这不…

什么是科技与艺术相结合的异形创意圆形(饼/盘)LED显示屏

在当今数字化与创意并重的时代,科技与艺术的融合已成为推动社会进步与文化创新的重要力量。其中,晶锐创显异形创意圆形LED显示屏作为这一趋势下的杰出代表,不仅打破了传统显示设备的形态束缚,更以其独特的造型、卓越的显示效果和广…

3谐振功率放大器的实际电路设计

1原理电路 下图是谐振功率放大器的原理电路,如果我们照着下图搭一个电路,会发现它可能实现不了功率放大?这是为什么? 2实际电路设计 2.1要注意直流馈电线路 馈电原则(馈电供电): 1)保证直流电流分量流过直流电源&…

基于SpringBoot+Vue的校园礼服装租赁系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

【Java 优选算法】双指针(下)

欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 有效三角形的个数 题目链接 解法 解法1:暴力枚举--->O(n^3) 解法2:利用单调性,使用双指针来解决---->O(n^2) 优化:对整个数组进行排序先固定最大数在最大数的左…

【模板代码的组织结构与模板的显式实例化和声明】模板代码的组织结构与模板的显式实例化和声明

一、模板的组织结构 之前对于模板,我们都是写在同一个 . c p p .cpp .cpp文件下,那如果我们将模板分开,单独开一个 . h .h .h和 . c p p .cpp .cpp来创建模板,会发生什么? 首先,我们创建一个 m y c l a s…

【2024.08】图模互补:知识图谱与大模型融合综述-笔记

阅读目的:假设已有一个知识图谱,如何利用图谱增强模型的问答,如何检索知识图谱、知识图谱与模型的文本如何相互交互、如何利用知识图谱增强模型回答的可解释性。 从综述中抽取感兴趣的论文进一步阅读。 来源:图模互补&#xff1…

阿里云 Quick BI使用介绍

Quick BI使用介绍 文章目录 阿里云 Quick BI使用介绍1. 创建自己的quick bi服务器2. 新建数据源3. 上传文件和 使用4. 开始分析 -选仪表盘5. 提供的图表6. 一个图表的设置使用小结 阿里云 Quick BI使用介绍 Quick BI是一款全场景数据消费式的BI平台,秉承全场景消费…

【梯度下降|链式法则】卷积神经网络中的参数是如何传输和更新的?

【梯度下降|链式法则】卷积神经网络中的参数是如何传输和更新的? 【梯度下降|链式法则】卷积神经网络中的参数是如何传输和更新的? 文章目录 【梯度下降|链式法则】卷积神经网络中的参数是如何传输和更新的?1. 什么是梯度?2.梯度…

华为eNSP使用详解

eNSP(Enterprise Network Simulation Platform)是华为提供的一款网络仿真平台,它允许用户在没有真实设备的情况下进行网络实验和学习网络技术。eNSP可以模拟各种网络设备,如交换机、路由器、防火墙等,并支持创建多种网…

Linux 环境打包 QT 程序

目录 前言 一、安装Qt库和编译器 二、编译生成可执行文件 三、准备脚本 四、添加程序依赖库 五、添加 Qt 相关依赖库 六、发布 deb 安装包 参考文章 前言 Qt是一个跨平台的GUI框架,可以在多种操作系统上运行,包括Linux、Windows和macOS。因此&am…

ASP.NET Core高效管理字符串集合

我们在开发 Web 项目时经常遇到需要管理各种来源的字符串集合(例如HTTP 标头、查询字符串、设置的值等)的情况。合理的管理这些字符串集合不仅可以减少出bug的几率,也能提高应用程序的性能。ASP.NET Core 为我们提供了一种特殊的只读结构体 S…

C/C++实现植物大战僵尸(PVZ)(打地鼠版)

🚀欢迎互三👉:程序猿方梓燚 💎💎 🚀关注博主,后期持续更新系列文章 🚀如果有错误感谢请大家批评指出,及时修改 🚀感谢大家点赞👍收藏⭐评论✍ 游戏…

【数据结构】字符串与JSON字符串、JSON字符串及相应数据结构(如对象与数组)之间的相互转换

前言&#xff1a; 下面打印日志用的是FastJSON依赖库中的 Log4j2。依赖&#xff1a; <!-- Alibaba Fastjson --> <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.80</version> …

[Redis] Redis中的set和zset类型

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…