文章目录
前言
本文主要记录:
1.map的实现原理。
2.拉链法原理。
3.map是否协程安全的。
4.sync.map的实现原理。
一、map的实现原理
1.数据结构:
go中的map是同时使用了多个数据结构结合哈希表来实现的。go使用runtime.hmap这个struct来表示map
type hmap struct {
// 存储的元素的个数
count int
flags uint8
// 当前哈希表拥有的桶的数量
B uint8
noverflow uint16
// hash时用到的值
hash0 uint32
// []bmap 桶数组
buckets unsafe.Pointer
oldbuckets unsafe.Pointer
nevacuate uintptr
extra *mapextra
}
type mapextra struct {
overflow *[]*bmap
oldoverflow *[]*bmap
nextOverflow *bmap
}
// bmap实际上是一个可以存8个元素的uint8数组,存储的是 高8位的hash值
type bmap struct {
tophash [bucketCnt]uint8
}
// 这里展示了更多的bmap数据结构的字段
type bmap struct {
topbits [8]uint8
keys [8]keytype
values [8]valuetype
pad uintptr
overflow uintptr
}
2.初始化:
map的初始化分为字面量(编译器)和运行期初始化两种方式。
2.1 字面量初始化方式:
1)当使用字面量进行初始化的key value键值对的个数小于25个时,底层实际上会一次性将所有的键值对都加进hashmap中。如下代码所示:
hash := make(map[string]int, 3)
hash["1"] = 2
hash["3"] = 4
hash["5"] = 6
- 当元素大于25个时,底层会使用两个切片,一个key的切片,一个value的切片,然后通过循环下标的方式对hash进行赋值操作。()
hash := make(map[string]int, 26)
vstatk := []string{
"1", "2", "3", ... , "26"}
vstatv := []int{
1, 2, 3, ... , 26}
for i := 0; i < len(vstatk); i++ {
hash[vstatk[i]] = vstatv[i]
}
2.2 运行时创建map的方式:
当创建的map容量比较小的时候,例如小于bucketSize 8 时,会在栈上创建,并且golang底层会使用如下的方式快速创建:
var h *hmap
var h1 hmap
var bv bmap
// 直接赋值初始化一个 hmap
h = &h1
b := &bv
// 初始化一个bmap
h.buckets = b
// 获得一个hash种子
h.hash0 = fashtrand0()
2.