golang map源码浅析 golang 中 mapgo语言源码编辑的实现结构为: 哈希表 + 链表 。其中链表,作用是当发生hash冲突时,拉链法生成的结点 。
可以看到,[]bmap是一个hash table,每一个 bmap是我们常说的“桶” 。经过hash 函数计算出来相同的hash值, 放到相同的桶中 。一个 bmap中可以存放 8个 元素,如果多出8个,则生成新的结点 , 尾接到队尾 。
以上是只是静态文件 src/runtime/map.go 中的定义 。实际上编译期间会给它加料,动态地创建一个新的结构:
上图就是 bmap的内存模型,HOB Hash指的就是 top hash 。注意到 key 和 value 是各自放在一起的,并不是key/value/key/value/...这样的形式 。源码里说明这样的好处是在某些情况下可以省略掉 padding 字段,节省内存空间 。
每个 bmap设计成 最多只能放 8 个 key-value 对 ,如果有第 9 个 key-value 落入当前的 bmap,那就需要再构建一个 bmap,通过overflow指针连接起来 。
map创建方法:
我们实际上是通过调用的makemap,来创建map的 。实际工作只是初始化go语言源码编辑了hmap中的各种字段,如:设置B的大?。?设置hash 种子 hash 0.
注意 :
makemap返回是*hmap指针,即map 是引用对象,对map的操作会影响到结构体内部。
使用方式
对应的是下面两种方法
map的key的类型,实现go语言源码编辑了自己的hash 方式 。每种类型实现hash函数方式不一样 。
key 经过哈希计算后得到hash值,共 64 个 bit 位 。其中后B 个bit位置,用来定位当前元素落在哪一个桶里, 高8个bit 为当前 hash 值的top hash 。实际上定位key的过程是一个双重循环的过程,外层循环遍历 所有的overflow,内层循环遍历 当前bmap 中的 8个元素。
举例说明: 如果当前 B 的值为 5,那么buckets 的长度 为 2^5 = 32 。假设有个key 经过hash函数计算后,得到的hash结果为:
外层遍历bucket 中的链表
内层循环遍历 bmap中的8个 cell
建议先不看此部分内容,看完后续修改 map中元素 - 扩容操作后 再回头看此部分内容 。
扩容前的数据:
等量扩容后的数据:
等量扩容后,查找方式和原本相同,不多做赘述 。
两倍扩容后的数据
两倍扩容后,oldbuckets 的元素,可能被分配成了两部分 。查找顺序如下:
此处只分析mapaccess1 , 。mapaccess2相比mapaccess1 多添加了是否找到的bool值,有兴趣可自行看一下 。
使用方式:
步骤如下:
扩容条件 :
扩容的标识 : h.oldbuckets != nil
假设当前定位到了新的buckets的3号桶中 , 首先会判断oldbuckets中的对应的桶有没有被搬迁过 。如果搬迁过了,不需要看原来的桶了,直接遍历新的buckets的3号桶 。
扩容前:
等量扩容结果
双倍扩容会将old buckets上的元素分配到x,y两个部key1B == 0 分配到x部分,key1B == 1 分配到y部分
注意: 当前只对双倍扩容描述,等量扩容只是重新填充了一下元素, 相对位置没有改变 。
假设当前map 的B == 5,原本元素经过hash函数计算的 hash 值为:
因为双倍扩容之后 B = B + 1 , 此时B == 6 。key1B == 1, 即 当前元素rehash到高位,新buckets中 y 部分. 否则 key1B == 0 则rehash到低位,即x 部分 。
使用方式:
可以看到,每一遍历生成迭代器的时候,会随机选取一个bucket 以及 一个cell开始 。从前往后遍历,再次遍历到起始位置时,遍历完成 。
如何创建,编译,打包go语言的源代码和工程1.最简单的方法:
public static String reverse1(String str)
{return new StringBuffer(str).reverse().toString();
推荐阅读
- html5购物完整源码,基于html的购物网站源码
- 荣耀50会上鸿蒙吗,荣耀50会升鸿蒙吗
- linux命令编码 linux命令设置编码
- 路由器管理里的日记是什么,路由器管理页面是什么意思
- jquery客户端缓存,jquery download方法
- 飞机模拟网页游戏下载,飞机模拟游戏中文版
- mysql怎么创建订单表 mysql自动生成订单号
- 罗永浩搞什么直播,罗永浩直播间是谁
- 需要网络的像素游戏,像素游戏网站