基本信息
文件名称:Golangmap实现原理深入分析.docx
文件大小:20.28 KB
总页数:8 页
更新时间:2025-05-22
总字数:约5.4千字
文档摘要

Golangmap实现原理深入分析

目录简介Map的底层内存模型Map的存与取底层代码寻址过程Map的扩容第一种情况第二种情况Map的有序性Map的并发

简介

本文主要通过探究在golang中map的数据结构及源码实现来学习和了解map的特性,共包含map的模型探究、存取、扩容等内容。欢迎大家共同讨论。

Map的底层内存模型

在goland的源码中表示map的底层struct是hmap,其是hashmap的缩写

typehmapstruct{

//map中存入元素的个数,golang中调用len(map)的时候直接返回该字段

countint

//状态标记位,通过与定义的枚举值进行操作可以判断当前是否处于这种状态

flagsuint8

Buint8//2^B表示bucket的数量,B表示取hash后多少位来做bucket的分组

noverflowuint16//overflowbucket的数量的近似数

hash0uint32//hashseed(hash种子)一般是一个素数

bucketsunsafe.Pointer//共有2^B个bucket,但是如果没有元素存入,这个字段可能为nil

oldbucketsunsafe.Pointer//在扩容期间,将旧的bucket数组放在这里,新buckets会是这个的两倍大

nevacuateuintptr//表示已经完成扩容迁移的bucket的指针,地址小于当前指针的bucket已经迁移完成

extra*mapextra//optionalfields

}

B是buckets数组的长度的对数,即bucket数组的长度是2^B。bucket的本质上是一个指针,指向了一片内存空间,其指向的struct如下所示:

//AbucketforaGomap.

typebmapstruct{

tophash[bucketCnt]uint8

}

但这只是表面(src/runtime/hashmap.go)的结构,编译期间会给它加料,动态地创建一个新的结构:

typebmapstruct{

topbits[8]uint8

keys[8]keytype

values[8]valuetype

paduintptr//内存对齐使用,可能不需要

overflowuintptr//当bucket的8个key存满了之后

}

bmap就是我们常说的桶的底层数据结构,一个桶中可以存放最多8个key/value,map使用hash函数得到hash值决定分配到哪个桶,然后又会根据hash值的高8位来寻找放在桶的哪个位置具体的map的组成结构如下图所示:

Map的存与取

在map中存与取本质上都是在进行一个工作,那就是:

查询当前k/v应该存储的位置。

赋值/取值,所以我们理解了map中key的定位我们就理解了存取。

底层代码

funcmapaccess2(t*maptype,h*hmap,keyunsafe.Pointer)(unsafe.Pointer,bool){

//map为空,或者元素数为0,直接返回未找到

ifh==nil||h.count==0{

returnunsafe.Pointer(zeroVal[0]),false

//不支持并发读写

ifh.flagshashWriting!=0{

throw(concurrentmapreadandmapwrite)

//根据hash函数算出hash值,注意key的类型不同可能使用的hash函数也不同

hash:=t.hasher(key,uintptr(h.hash0))

//如果B=5,那么结果用二进制表示就是11111,返回的是B位全1的值

m:=bucketMask(h.B)

//根据hash的后B位,定位在bucket数组中的位置

b:=(*bmap)(unsafe.Pointer(uintptr(h.buckets)+(hashm)*uintptr(t.bucketsize)))

//当h.oldbuckets