详细解读go语言中的map
Map
map底层是由哈希表实现的
Go使用链地址法来解决键冲突。
map本质上是一个指针,指向hmap

这里的buckets就是桶,bmap
每一个bucket最多可以放8个键值对,但是为了让内存排列更加紧凑,8个key放一起,8个value放一起。在8个key前面是8个tophash,每个tophash都是对应哈希值的高8位,最后由一个overflow字段指向下一个bmap,就是溢出桶。溢出桶内存布局和常规桶一样,bmap内存布局如图

如果哈希表要分配的桶的数目大于24,就会预分配2(B-4)个溢出桶备用。
这些溢出桶和常规桶在内存中是连续的,前2^B个用作常规桶,后面的用作溢出桶
查找过程
- 查找或者操作map时,首先key经过hash函数生成hash值
- 通过哈希值的低8位来判断当前数据属于哪个桶(bucket)
- 找到bucket以后,通过哈希值的高八位与bucket存储的高位哈希值循环比对
- 如果相同就比较刚才找到的底层数组的key值,如果key相同,取出value。
- 如果高八位hash值在此bucket没有,或者有,但是key不相同,就去链表中下一个溢出bucket中查找,直到查找到链表的末尾。
- 如果查找不到,也不会返回空值,而是返回相应类型的0值。
插入过程
新元素插入过程如下:
- 根据key值算出哈希值
- 取哈希值低位与hmap.B取模确定bucket位置
- 查找该key是否已经存在,如果存在则直接更新值
- 如果没找到将key,将key插入。
map扩容机制
为了保证访问效率,当新元素将要添加进map时,都会检查是否需要扩容,扩容实际上是以空间换时间的手段。
触发扩容的条件有二个:
负载因子 > 6.5时,也即平均每个bucket存储的键值对达到6.5个。
负载因子 = 键数量/bucket数量
溢出桶(overflow)数量 > 2^15时,也即overflow数量超过32768时。
- 增量扩容
如果负载因子>6.5时,进行增量扩容。这时会新建一个桶(bucket),新的bucket长度是原来的2倍,然后旧桶数据搬迁到新桶。每个旧桶的键值对都会分流到两个新桶中
考虑到如果map存储了数以亿计的key-value,一次性搬迁将会造成比较大的延时,Go选择“渐进式扩容”,先分配2倍内存空间的新桶,然后标记当前哈希表正在扩容,每次访问map时都会触发一次搬迁,这样可以把扩容消耗的时间分散到多次操作中。
- 等量扩容
负载因子没超标,溢出桶较多
如果常规桶数目不大于2^15,那么使用的溢出桶数目超过常规桶就算是多了;
如果常规桶数目大于215,那么使用溢出桶数目一旦超过215就算多了。
所谓等量扩容,就是创建和旧桶数目一样多的新桶,然后把原来的键值对迁移到新桶中,重新做一遍类似增量扩容的搬迁动作。这样做的目的是把松散的键值对重新排列一次,能够存储的更加紧凑,进而减少溢出桶的使用,以使bucket的使用率更高,进而保证更快的存取。
详细解读go语言中的map的更多相关文章
- 详细解读go语言中的chnanel
Channel 底层数据结构 type hchan struct { qcount uint // 当前队列中剩余元素个数 dataqsiz uint // 环形队列长度,即可以存放的元素个数 buf ...
- 【翻译】go语言中的map实战
业余时间翻译,水平很差,如有瑕疵,纯属无能. 原文链接 http://blog.golang.org/go-maps-in-action go语言中的map实战 1. 简介 哈希表是计算机科学中最重要 ...
- Go语言中的map(十一)
map是一种无序的基于 key-value 的数据结构,Go语言中的map是引用类型,所以跟切片一样需要初始化才能使用. 定义map 定义 map 的语法如下: map[keyType]ValueTy ...
- Go语言中的map
map是一个集合,可以使用类似处理数组和切片的方式迭代map中的元素.但map是无序的集合.无序的原因是map的实现使用了散列表. map的创建并初始化主要是两种方式: 1.内置的make函数 2.使 ...
- C语言中的static 详细分析
转自:http://blog.csdn.net/keyeagle/article/details/6708077/ google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大 ...
- 【转载】C语言中的static 详细分析
原blog地址:http://blog.csdn.net/keyeagle/article/details/6708077/ google了近三页的关于C语言中static的内容,发现可用的信息很少, ...
- [转]Go语言中的make和new
前言 本文主要给大家介绍了Go语言中函数new与make的使用和区别,关于Go语言中new和make是内建的两个函数,主要用来创建分配类型内存.在我们定义生成变量的时候,可能会觉得有点迷惑,其实他们的 ...
- Attention is all you need 详细解读
自从Attention机制在提出之后,加入Attention的Seq2Seq模型在各个任务上都有了提升,所以现在的seq2seq模型指的都是结合rnn和attention的模型.传统的基于RNN的Se ...
- 转:C语言中的static变量和C++静态数据成员(static member)
转自:C语言中的static变量和C++静态数据成员(static member) C语言中static的变量:1).static局部变量 a.静态局部变量在函数内定义,生存期为整个程序 ...
随机推荐
- pycharm基础使用入门
pycharm基础使用入门 输出 print函数 print('hello world') 右键选择run或者右上角的三角形运行,可以运行出结果 "E:\all sorts of learn ...
- 有语言基础的人应该如何学习python?
正好最近在学python,感觉有语言基础的话更多在乎一些语法糖,毕竟其他东西在之前应该接触过了. 笔者C++是起始语言,也接触过java.js,介绍一点python的特点吧.帮助自己巩固所学,也希望能 ...
- tomcat与springmvc 结合 之---第20篇 springmvc 对于加载的bean对象存储在哪里
是
- 版本号是通过import合并而来的,不是继承来的
- Xshell 打开时,初始运行卡慢优化方法
我使用的是Xshell 6免费版,有需要的同学可以去这个地址下载:https://www.netsarang.com/download/down_form.html?code=622 一开始安装完Xs ...
- js 倒计时退出
<p>您已提交成功!<span id='submit_second'></span></p> $("#submit_second") ...
- [.NET大牛之路 005] .NET 的执行模型
.NET大牛之路 • 王亮@精致码农 • 2021.07.06 前面我们介绍 .NET 历史时讲过,微软基于 .NET Framework 重新设计并创造了跨平台的 .NET Core,目前已经发展到 ...
- 运行第一个程序!hello world!
第一个程序:1 //打印 hello world 2 3 #include <stdio.h> 4 int main() 5 { 6 printf("hello world!\n ...
- Git常见的操作及命令
Git的下载安装 安装直接去Git官方网站https://git-scm.com/下载后安装即可. Git常用命令 git init初始化本地仓库(没有仓库就创建) git clone克隆远程仓库到本 ...
- 『Java』数组
在学习数组之前先学习java.util.Arrays类中的一个静态方法Arrays.toString(). 该方法可以将传入的数组格式化为一个字符串,便于我们查看数组内容,例如: import jav ...