【Go】 map 精髓理解

这篇具有很好参考价值的文章主要介绍了【Go】 map 精髓理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

map

go map 的底层结构 hmap,的四个元素
然后再讲一下 buckets 的元素,讲一下 hash 冲突,和解决方法
再讲一下,增量扩容和等量扩容
再讲一下增删改查的过程,就查询过程

map 基础

向值为 nil 的 map 添加元素会发生 panic,说白了就是给声明但是没初始化的map添加元素
查询时候,如果没这个元素,会返回对应的零值,例如 int 的零值 0
一开始用 make 初始化时候,指定容量,可以有效的减少内存分配的次数,提升性能
查询语法if v, ok := m[“apple”];ok {}
删除语法:delete(m,“apple”) 空也不报错
添加和修改是一样的就是:m[“apple”] = “red”
len 也可以查长度
不是原子性的,并发不安全,多个协程同时操作一个 map 会产生读写冲突,panic,不过可以通过加锁或者 sync.map 实现并发安全

实现原理

1、数据结构

使用 Hash 表作为底层实现,一个 Hash 表可以有多个 bucket,每个 bucket 保存了一个或多个键值对,像,redis 是默认一个,go 是6.5个,多了会扩容
我们常说的 hash 桶就是 bucket,
【Go】 map 精髓理解,golang,开发语言,后端

type Map struct {
	Key  *Type
	Elem *Type
	Bucket *Type
	Hmap   *Type
	Hiter  *Type
}

// hmap 简化版本
type hmap struct {
	count     int   // 元素个数,kv个数
	B         uint8 // bucket 数组大小,其实就是 bmap
	buckets    unsafe.Pointer // 指针指向 bucket 数组,数组长 2 的 B 次方
	oldbuckets unsafe.Pointer // 老旧 bucket 数组,用于扩容
  ......
}

type bmap struct {
  tophash [bucketCnt]uint8 // 存储 hash 值的高 8 位,就是长度为 8 的整型数组
  date []byte // kkkkkkvvvvvv数据,这样可以节省内存,避免了内存对齐
  overflow *bmap // 溢出 bucket 的地址,指向了下一个 bucket 将冲突的键连接起来
}

2、hash 冲突

当两个或以上数量的键被 hash 到同一个 bucket 时候,我们称这些键发生了冲突
Go 使用了链地址法解决冲突
因为每个 bucket 可以存放八个键值对,当同一个 bucket 存放超过 8 个键值对时候就会创建一个 bucket,用类似链表的方式将 bucket 连接起来,使用 *bucket 指向溢出的那部分

3、负载因子

负载因子 = 键数量 / bucket 数量
负载因子过大过小都不好
-过小,空间利用率较小
-过大,冲突严重,存取效率低

当负载因子过大,需要申请更多的 bucket 并且对所有的键值对重新排列,使其均匀的分配在 bucket,这个过程叫 rehash
每个 hash 表对负载因子的容忍度大小不一样,go 6.5 因为go的 bucket 可以存八个,redis 是1个

4、扩容

扩容有增量扩容,和等量扩容,都是对于负载因子过大的情况,对 go 就是当一个 bucket 有7/8个元素时候
增量扩容:
会新建一个 bucket ,新的 bucket 大小是原来两倍,然后把旧的 bucket 数组中的数据逐渐的搬迁到新的 bucket 数组
不过可能出现一个情况,当有数亿个元素时候,考虑到一次性搬迁会有很大延时,所有 go 用逐级搬迁策略,就是每次访问 map 都会搬迁一下,两个键值对
那它是如何处理数组指针呢?就是让 hmap 中 oldbuckets 指针指向原 buckets 数组,然后申请新的 buckets 数组,长度为原来两倍,之后的迁移工作就是将 oldbuckets 元素逐渐搬迁到新的 buckets ,搬迁完毕就可以释放 oldbuckets 数组
等量扩容:
这个不扩容,甚至内存释放了一些,意思是,不扩容的情况下,实行了类似增量扩容中搬迁的动作
因为在某些情况下,极端情况,我们删除了一些元素,很多哈,导致最后的元素集中于一个 bucket ,而这个 bucket 有很多的溢出 bucket 桶,删除后这些桶都空了,这时候等量扩容,就是使得 bucket 数量不变,但是重新组织后的 overflow 的 Bucket 数量减少,节省了空间,提高了查询效率

5、查询

查询就是:根据键的 Hash 值确定一个 bucket ,查它看有没有该元素
查询:
根据 key 值计算 Hash 值
取 Hash 值低位与 hmap.B 取模确定 bucket 位置
取 Hash 值高位,在 tophash 数组查询,如果tophash[i]的hash值与计算的一样,就去获取 tophash[i] 的key值
当前 bucket 没找到就去溢出bucket找
如果当前处于搬迁中,优先从 oldbucket 找
那么添加删除都差不多了,就不赘述了

参考自:《Go 专家编程》文章来源地址https://www.toymoban.com/news/detail-602543.html

到了这里,关于【Go】 map 精髓理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Golang】VsCode下开发Go语言的环境配置(超详细图文详解)

    📓推荐网站(不断完善中):个人博客 📌个人主页:个人主页 👉相关专栏:CSDN专栏、个人专栏 🏝立志赚钱,干活想躺,瞎分享的摸鱼工程师一枚 ​ 话说在前,Go语言的编码方式是 UTF-8 ,理论上你直接使用文本进行编辑也是可以的,当然为了提升我们的开发效率我们还是需

    2024年02月07日
    浏览(86)
  • golang 中 go func() {} 理解

    在Golang 中,go func() {} 表示创建一个新的 Goroutine(轻量级线程),用于异步执行函数。 具体来说,go func() {} 创建了一个匿名函数(即没有函数名的函数),并在其前面加上 go,以表示该函数应该在一个新的 Goroutine 中异步执行。因此,当程序执行到该语句时,它会立即

    2024年02月15日
    浏览(41)
  • 深入理解 go sync.Map - 基本原理

    我们知道,go 里面提供了 map 这种类型让我们可以存储键值对数据,但是如果我们在并发的情况下使用 map 的话,就会发现它是不支持并发地进行读写的(会报错)。 在这种情况下,我们可以使用 sync.Mutex 来保证并发安全,但是这样会导致我们在读写的时候,都需要加锁,这

    2024年01月18日
    浏览(41)
  • 【Go】Go 语言教程--Go 语言Map(集合)(十六)

    往期回顾: Go 语言教程–介绍(一) Go 语言教程–语言结构(二) Go 语言教程–语言结构(三) Go 语言教程–数据类型(四) Go 语言教程–语言变量(五) Go 语言教程–GO语言常量(六) Go 语言教程–GO语言运算符(七) Go 语言教程–GO条件和循环语句(八) Go 语言教程

    2024年02月16日
    浏览(46)
  • go语言(八)---- map

    map的声明方式有以下三种。 map的使用方式 map的增删改查 map的传参

    2024年01月20日
    浏览(40)
  • Go 语言Map(集合)

    Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。 Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。 定义 Map 可以

    2024年02月05日
    浏览(39)
  • Golang:Go语言结构

    在我们开始学习 Go 编程语言的基础构建模块前,让我们先来了解 Go 语言最简单程序的结构。 Go 语言的基础组成有以下几个部分: 包声明 引入包 函数 变量 语句 表达式 注释 接下来让我们来看下简单的代码,该代码输出了\\\"Hello World!\\\": 让我们来看下以上程序的各个部分: 第一

    2024年02月10日
    浏览(59)
  • golang实现webgis后端开发

    目录 前言 二、实现步骤 1.postgis数据库和model的绑定 2.将pg库中的要素转换为geojson (1)几何定义 (2)将wkb解析为几何类型 (3)定义geojson类型 (4)数据转换 (5)数据返回  2.前端传入的geojson储存到数据库 3、其他功能实现 总结         停更了接近一个月都在研究一门新语言gola

    2024年02月08日
    浏览(50)
  • 前端vue后端go如何进行跨域设置?一篇就通透理解

    跨域 (Cross-Origin)指的是在 浏览器 中,当一个 web 应用程序试图访问 不同域名、不同端口或不同协议的资源时,就会发生跨域请求 ,此时浏览器的 同源策略 (Same-Origin Policy)就会进行拦截,他是同源策略是一种 安全机制 ,它限制了网页中的 JavaScript 代码只能访问同源(

    2024年04月12日
    浏览(47)
  • Go语言基础之map

    Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现。 map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。 map定义 Go语言中 map的定义语法如下: 其中, KeyType:表示键的类型。 ValueType:表示键对应的值的类型。 map类型的变量

    2024年02月11日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包