go语言中的map是一种内建引用类型
map存储时key不可重复,无顺序,排序的话可以将key排序,然后取出对应value.只有可以比较的类型才可以作key,value则无限制.
go中的map采用的是哈希map
给定key后,会通过哈希算法计算一个哈希值,低B位(这里是大写的B,2^B表示当前map中bucket的数量)代表的是存在map中的哪一个bucket,高8位则是了存在bucket中的一个uint[8]数组中,高8位所在的数组indec可用来寻找对应key的index.
map可抽象为bucket结构体组成的结构体,bucket数量一开始是固定的,后期不够用之后会进行扩容,bucket内部含有数组,bucket内部数组存储的即为key和value,为8组数据,存储方式为key0,key1,key2…value0,value1,value2…形式,而不是key和value顺次存储,这样是为了防止key和vaule长度不一致时需要额外padding.key和value存储在同一个数组中.
bucket的结构
type hmap struct {
count int // # 元素个数
flags uint8
B uint8 // 说明包含2^B个bucket
noverflow uint16 // 溢出的bucket的个数
hash0 uint32 // hash种子
buckets unsafe.Pointer // buckets的数组指针
oldbuckets unsafe.Pointer // 结构扩容的时候用于复制的buckets数组
nevacuate uintptr // 搬迁进度(已经搬迁的buckets数量)
extra *mapextra
}
bucket的数据结构
type bmap struct {
// tophash generally contains the top byte of the hash value
// for each key in this bucket. If tophash[0] < minTopHash,
// tophash[0] is a bucket evacuation state instead.
tophash [bucketCnt]uint8
// Followed by bucketCnt keys and then bucketCnt values.
// NOTE: packing all the keys together and then all the values together makes the
// code a bit more complicated than alternating key/value/key/value/... but it allows
// us to eliminate padding which would be needed for, e.g., map[int64]int8.
// Followed by an overflow pointer.
}
需要注意的是,bucket中不止有tophash数组,里面存储的是key通过hash函数算出来的哈希值的高8位,数组长度为8,对应了存储key和value的字节数组的index…注意后面几行注释,hmap并非只有一个tophash,而是后面紧跟8组kv对和一个overflow的指针,这样才能使overflow成为一个链表的结构。但是这两个结构体并不是显示定义的,而是直接通过指针运算进行访问的。
kv的存储形式为”key0key1key2key3…key7val1val2val3…val7″,这样做的好处是:在key和value的长度不同的时候,节省padding空间。如上面的例子,在map[int64]int8中,4个相邻的int8可以存储在同一个内存单元中。如果使用kv交错存储的话,每个int8都会被padding占用单独的内存单元(为了提高寻址速度)。
一个完整的map中key查找的流程:
1.根据传入的key用对应的hash函数算出哈希值.
2.取哈希值的低B位定为到是哪一个bucket
3.定位到bucket之后,取哈希值的高8位,和bucket中的uint[8]数组中存储的高8位进行比对,完全匹配根据数组的inidex在key,value字节数组中查找对应的key,若匹配上则返回key,value,若没有完全匹配则继续比对高8位的值,当前bucket的高8位数组全部比对完,若有溢出bucket则继续比对,若全部比对完未发现则当前map不含有此key.