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.