Golang中实现图底层的原理是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
<强> 1。数据结构及内存管理
hashmap的定义位于src/运行/hashmap。中,首先我们看下hashmap和桶的定义:
hmap struct类型{ 计数int//元素的个数 旗帜uint8//状态标志 B uint8//可以最多容纳6.5 * 2 ^ B个元素,6.5为装载因子 noverflow uint16//溢出的个数 hash0 uint32//哈希种子 桶不安全。指针//桶的地址 oldbuckets不安全。//指针旧桶的地址,用于扩容 nevacuate uintptr//搬迁进度,小于nevacuate的已经搬迁 溢出* [2]* []* bmap }
其中,溢出是一个指针,指向一个元素个数为2的数组,数组的类型是一个指针,指向一个切片,切片的元素是桶(bmap)的地址,这些桶都是溢出桶;为什么有两个?因为去地图在哈希冲突过多时,会发生扩容操作,为了不全量搬迁数据,使用了增量搬迁,[0]表示当前使用的溢出桶集合,[1]是在发生扩容时,保存了旧的溢出桶集合;溢出存在的意义在于防止溢出桶被gc。
//一桶去地图。 bmap struct类型{//每个元素散列值的高8位,如果tophash [0] & lt;minTopHash,表示这个桶的搬迁状态 tophash bucketCnt uint8//接下来是8个关键,8个值,但是我们不能直接看到;为了优化对齐,去采用了钥匙放在一起,价值放在一起的存储方式,//再接下来是哈希冲突发生时,下一个溢出桶的地址 }
tophash的存在是为了快速试错,毕竟只有8位,比较起来会快一点。
从定义可以看的出,不同于STL中地图以红黑树实现的方式,Golang采用了散列表的实现,解决冲突采用的是链地址法。也就是说,使用数组+链表来实现地图。特别的,对于一个键,几个比较重要的计算公式为:
keyhashhashtopbucket indexkeyhash:=alg。散列(关键uintptr (h.hash0)):=uint8(散列在祝辞(系统。PtrSize * 8 - 8))桶:=散列,(uintptr (1) & lt; & lt; h。B - 1),即哈希% 2 B ^例如,对于B=3,当哈希(关键)=4时,hashtop=0,桶=4,当哈希(关键)=20时,hashtop=0,桶=4;这个例子我们在搬迁过程还会用的到。
内存布局类似于这样:
hashmap-buckets
<强> 2。创建——makemap
地图的创建比较简单,在参数校验之后,需要找到合适的B来申请桶的内存空间,接着便是穿件hmap这个结构,以及对它的初始化。
makemap
<强> 3。访问——mapaccess
对于给定的一个关键,可以通过下面的操作找到它是否存在
形象。png
方法定义为
//返回键,如果没有找到,返回零 func mapaccess1 (t * maptype h * hmap,关键unsafe.Pointer) unsafe.Pointer//返回键和存在。如果没有找到,返回nil,假的 func mapaccess2 (t * maptype h * hmap,关键unsafe.Pointer)(不安全。指针,bool)//返回键和值。如果没有找到,返回零,零 func mapaccessK (t * maptype h * hmap,关键unsafe.Pointer)(不安全。指针,unsafe.Pointer)
可见在找不到对应的关键的情况下,会返回零
<强> 4。分配,mapassign
为一个关键分配空间的逻辑,大致与查找类似,但增加了写保护和扩容的操作,注意,分配过程和删除过程都没有在oldbuckets中查找,这是因为首先要进行扩容判断和操作,如下:
指定
<代码>扩容是整个hashmap的核心算法,我们放在第6部分重点研究。
新建一个溢出桶,并将其拼接在当前桶的尾部,实现了类似链表的操作:
//获取当前桶的溢出桶 func (b * bmap)溢出(t * maptype) * bmap { 返回* (* * bmap)(添加(unsafe.Pointer (b), uintptr (t.bucketsize) -sys.PtrSize)) }//设置当前桶的溢出桶 func (h * hmap) setoverflow (t * maptype, b, ovf * bmap) { h.incrnoverflow () 如果t.bucket.kind& kindNoPointers !=0 { h.createOverflow ()//重点,这里讲溢出桶附加到溢出[0]的后面 * h。溢出[0]=append (* h。溢出[0],ovf) } * (* * bmap)(添加(unsafe.Pointer (b), uintptr (t.bucketsize) -sys.PtrSize))=ovf }