目录

  • 写在前面
  • 哈希表
  • 如何解决哈希冲突的问题?
    • 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相关推荐

  1. golang对map的理解

    一.map的基本介绍 map 是 key-value 数据结构,又称为字段或者关联数组.类似其它编程语言的集合 二.map的声明 var map 变量名 map[keytype]valuetyp ma ...

  2. 深入理解Golang之context

    深入理解Golang之context context是Go并发编程中常用到一种编程模式.本文将从为什么需要context,深入了解context的实现原理,以了解如何使用context. 作者:Tur ...

  3. Golang sync.Map 原理(两个map实现 读写分离、适用读多写少场景)

    参考: 由浅入深聊聊Golang的sync.Map 通过对源码的逐行分析,清晰易懂 Golang sync.Map原理 通过向 sync.Map 中增删改查来介绍sync.Map的底层原理 Golan ...

  4. golang 中 map 转 struct

    golang 中 map 转 struct package mainimport ("fmt""github.com/goinggo/mapstructure" ...

  5. 深入理解Golang 编程思维和工程实战

    | 导语 Golang 的一些编程思维和思想,以及总结一些常见的优雅编程实战技巧 目录 一 Golang 编程思维 二 Golang 高级编码技巧 1 优雅的实现构造函数编程思想 2 优雅的实现继承编 ...

  6. golang key map 所有_Map的底层实现 为什么遍历Map总是乱序的

    Golang中Map的底层结构 其实提到Map,一般想到的底层实现就是哈希表,哈希表的结构主要是Hashcode + 数组. 存储kv时,首先将k通过hashcode后对数组长度取余,决定需要放入的数 ...

  7. Golang笔记——map

    map 的基本介绍 map 是 key-value 数据结构,又称为字段或者关联数组.类似其它编程语言的集合, 在编程中是经常使用到 map 的声明 基本语法 var map 变量名 map[keyt ...

  8. GoLang之map底层系列二(浅显学习)

    文章目录 GoLang之map底层系列二(浅显学习) 1.map++ 2.map引用传递 3.map不是并发安全 4.map的value为空接口 5.map的value为切片 6.value,ok=m ...

  9. GoLang之Map深度讲解

    文章目录 GoLang之Map深度讲解 1.Map查找 2.哈希函数 3.Map插入.修改 4.扩容分析 GoLang之Map深度讲解 1.Map查找 //以下常量在runtime/map.go里 c ...

最新文章

  1. 卫星图像中的车辆分析--A Large Contextual Dataset for Classification, Detection and Counting of Cars
  2. ibatis教程之调用带参数的存储过程
  3. python三维图的坐标_六维图见过么?Python 画出来了
  4. 使用nsenter进入docker namespace
  5. HTML5唐四薪,8文件存取组件课件.ppt
  6. 【Qt】Qt数据库驱动层
  7. spring解析配置文件(三)
  8. Esxi直通板载Sata
  9. Shell 语法之函数
  10. 拓端tecdat|R语言对NASA元数据进行文本挖掘的主题建模分析
  11. CREO:CREO软件之零件【模型】扫描之扫描、螺旋扫描、可变剖面扫描、扫描混合、混合、边界混合、可变剖面扫描的简介及其使用方法(图文教程)之详细攻略
  12. 使用Pytorch搭建CNN
  13. latex不等于符号
  14. 跨境电商独立站海外引流渠道:Quora运营技巧
  15. Android自定义ImageView圆角
  16. 机器学习基础:最大似然(Maximum Likelihood) 和最大后验估计 (Maximum A Posteriori Estimation)的区别
  17. Java课程中心练习题
  18. WV.12-枚举-平面点的对称点
  19. RPG游戏-小地图系统
  20. 单双人贪吃蛇小游戏(控制台)

热门文章

  1. 璇玑图(后续的字符串处理)
  2. 怎样用文言文优雅地装逼!28万行唐诗中找出对称矩阵
  3. 深度学习目标检测论文1(YOLOv1论文的翻译)
  4. 基于SSM的学生信息管理系统
  5. 520告白网站的搭建
  6. Voyager下的Dashboard Widgets
  7. 《大话西游》你真的看懂了吗,kotlin命令行编译
  8. c语言tab什么意思_速收藏 | 学习C语言最需要记住的基础知识!!
  9. Licode—基于webrtc的SFU/MCU实现
  10. PentestBox中部分工具整理