深入理解Golang之Map
目录
- 写在前面
- 哈希表
- 如何解决哈希冲突的问题?
- 1.链表地址法
- 2.开放地址法
- 开放地址—线性探测法
- 开放地址—平方探测法
- 开放地址—双哈希
- Go Map实现
- map数据结构
- map的结构体为hmap
- bmap结构体(map的桶)
- Map扩容
- 触发扩容的条件:
- 增量扩容
- 等量扩容
写在前面
Map的实现主要有两种方式:哈希表(hash table)和搜索树(search tree)。例如Java中的hashMap是基于哈希表实现,而C++中的Map是基于一种平衡搜索二叉树——红黑树而实现的。Go中map的基于哈希表(也被称为散列表)实现。
哈希表
哈希表通常会有一堆桶来存储键值对,一个键值对会选择一个桶,怎么选择?
先通过哈希函数把键处理一下,得到一个哈希值,通过这个哈希值去选择相对应的桶
取模法:hash%(桶的个数m)得到一个桶编号
与运算法:hash&(m-1)
要限制桶的个数m必须是2的整数次幂
如何解决哈希冲突的问题?
1.链表地址法
写结构体的时候加入next指针
遇到冲突时,将数据写到next的位置
下面以一个简单的哈希函数 H(key) = key MOD 7(除数取余法)对一组元素 [50, 700, 76, 85, 92, 73,101] 进行映射,通过图示来理解链地址法处理Hash冲突的处理逻辑。
2.开放地址法
把其他下标的地址都对外开放
开放地址的方法:1.线性探测法2.平方探测(二次方探测)3.双哈希
开放地址—线性探测法
如果遇到冲突,就往下一个地址寻找空位,新位置=原始位置+i ( i是查找的次数 )
其中15,2,28,4进行模运算,都为2,遇到冲突,就会往下一个地址寻找空位
12,38进行模运算,都为12,遇到冲突,就会往下一个地址寻找空位
开放地址—平方探测法
如果遇到冲突,就往(原地址 + i ² )的位置寻找空位,新位置=原始位置+ i ² ( i是查找的次数 )
开放地址—双哈希
要设置第二个哈希的函数,例如:hash2(key)=R-(key mod R)
R要取比数组尺寸小的指指数
例如R取7 hash2(关键字)=7-(关键字%7)
如果遇到冲突,新位置=原始位置+i*hash2(关键字)
Go Map实现
map数据结构
map中的数据被存放于一个数组中的,数组的元素是桶(bucket),每个桶至多包含8个键值对数据。哈希值低位(low-order bits)用于选择桶,哈希值高位(high-order bits)用于在一个独立的桶中区别出键。哈希值高低位示意图如下
Go语言中的map是也基于哈希表实现的,它解决哈希冲突的方式是链地址法,即通过使用数组+链表的数据结构来表达map
map的结构体为hmap
// A header for a Go map.
type hmap struct {count int // 代表哈希表中的元素个数,调用len(map)时,返回的就是该字段值。flags uint8 // 状态标志,下文常量中会解释四种状态位含义。B uint8 // buckets(桶)的对数log_2(哈希表元素数量最大可达到装载因子*2^B)noverflow uint16 // 溢出桶的大概数量。hash0 uint32 // 哈希种子buckets unsafe.Pointer // 指向buckets数组的指针,数组大小为2^B,如果元素个数为0,它为nil。oldbuckets unsafe.Pointer // 如果发生扩容,oldbuckets是指向老的buckets数组的指针,老的buckets数组大小是新的buckets的1/2。非扩容状态下,它为nil。nevacuate uintptr // 表示扩容进度,小于此地址的buckets代表已搬迁完成。extra *mapextra // 这个字段是为了优化GC扫描而设计的。当key和value均不包含指针,并且都可以inline时使用。extra是指向mapextra类型的指针。
bmap结构体(map的桶)
// A bucket for a Go map.
type bmap struct {// tophash包含此桶中每个键的哈希值最高字节(高8位)信息(也就是前面所述的high-order bits)。// 如果tophash[0] < minTopHash,tophash[0]则代表桶的搬迁(evacuation)状态。tophash [bucketCnt]uint8
}
在8个键值对数据后面有一个overflow指针,因为桶中最多只能装8个键值对,如果有多余的键值对落到了当前桶,那么就需要再构建一个桶(称为溢出桶),通过overflow指针链接起来。溢出桶的内存布局和常规桶相同,是为了减少扩容次数而引入的,当一个桶存满了还有可用的溢出桶时,就会在桶后面链一个溢出桶
Map扩容
触发扩容的条件:
1.负载因子 > 6.5时,也即平均每个bucket存储的键值对达到6.5个,进行增量扩容
2.overflow数量 > 2^15时,也即overflow数量超过32768时,进行等量扩容
增量扩容
当负载因子大于6.5时,就新建一个bucket桶,新的bucket长度是原来的2倍,然后旧bucket数据搬迁到新的bucket。
等量扩容
所谓等量扩容,实际上并不是扩大容量,buckets数量不变,重新做一遍类似增量扩容的搬迁动作,把松散的键值对重新排列一次,以使bucket的使用率更高,进而保证更快的存取。
在极端场景下,比如不断地增删,而键值对正好集中在一小部分的bucket,这样会造成overflow的bucket数量增多,但负载因子又不高,从而无法执行增量搬迁的情况
深入理解Golang之Map相关推荐
- golang对map的理解
一.map的基本介绍 map 是 key-value 数据结构,又称为字段或者关联数组.类似其它编程语言的集合 二.map的声明 var map 变量名 map[keytype]valuetyp ma ...
- 深入理解Golang之context
深入理解Golang之context context是Go并发编程中常用到一种编程模式.本文将从为什么需要context,深入了解context的实现原理,以了解如何使用context. 作者:Tur ...
- Golang sync.Map 原理(两个map实现 读写分离、适用读多写少场景)
参考: 由浅入深聊聊Golang的sync.Map 通过对源码的逐行分析,清晰易懂 Golang sync.Map原理 通过向 sync.Map 中增删改查来介绍sync.Map的底层原理 Golan ...
- golang 中 map 转 struct
golang 中 map 转 struct package mainimport ("fmt""github.com/goinggo/mapstructure" ...
- 深入理解Golang 编程思维和工程实战
| 导语 Golang 的一些编程思维和思想,以及总结一些常见的优雅编程实战技巧 目录 一 Golang 编程思维 二 Golang 高级编码技巧 1 优雅的实现构造函数编程思想 2 优雅的实现继承编 ...
- golang key map 所有_Map的底层实现 为什么遍历Map总是乱序的
Golang中Map的底层结构 其实提到Map,一般想到的底层实现就是哈希表,哈希表的结构主要是Hashcode + 数组. 存储kv时,首先将k通过hashcode后对数组长度取余,决定需要放入的数 ...
- Golang笔记——map
map 的基本介绍 map 是 key-value 数据结构,又称为字段或者关联数组.类似其它编程语言的集合, 在编程中是经常使用到 map 的声明 基本语法 var map 变量名 map[keyt ...
- GoLang之map底层系列二(浅显学习)
文章目录 GoLang之map底层系列二(浅显学习) 1.map++ 2.map引用传递 3.map不是并发安全 4.map的value为空接口 5.map的value为切片 6.value,ok=m ...
- GoLang之Map深度讲解
文章目录 GoLang之Map深度讲解 1.Map查找 2.哈希函数 3.Map插入.修改 4.扩容分析 GoLang之Map深度讲解 1.Map查找 //以下常量在runtime/map.go里 c ...
最新文章
- 卫星图像中的车辆分析--A Large Contextual Dataset for Classification, Detection and Counting of Cars
- ibatis教程之调用带参数的存储过程
- python三维图的坐标_六维图见过么?Python 画出来了
- 使用nsenter进入docker namespace
- HTML5唐四薪,8文件存取组件课件.ppt
- 【Qt】Qt数据库驱动层
- spring解析配置文件(三)
- Esxi直通板载Sata
- Shell 语法之函数
- 拓端tecdat|R语言对NASA元数据进行文本挖掘的主题建模分析
- CREO:CREO软件之零件【模型】扫描之扫描、螺旋扫描、可变剖面扫描、扫描混合、混合、边界混合、可变剖面扫描的简介及其使用方法(图文教程)之详细攻略
- 使用Pytorch搭建CNN
- latex不等于符号
- 跨境电商独立站海外引流渠道:Quora运营技巧
- Android自定义ImageView圆角
- 机器学习基础:最大似然(Maximum Likelihood) 和最大后验估计 (Maximum A Posteriori Estimation)的区别
- Java课程中心练习题
- WV.12-枚举-平面点的对称点
- RPG游戏-小地图系统
- 单双人贪吃蛇小游戏(控制台)