GO高阶 の map详解
文章目录
一. map 基础操作
map 的声明
m := make(map[string]int, 2)
m["hah"] = 15
m["mmm"] = 20
m["wwm"] = 30
m["wsegewwm"] = 40
fmt.Println(m)
m := map[string]int{}
m["hah"] = 15
m["mmm"] = 20
m["wwm"] = 30
m["wsegewwm"] = 40
fmt.Println(m)
输出 map 中的键值对的个数
len 中输出的是键值对的个数,而不是容量的个数。
a := map[string]int{"15": 15, "16": 16, "17": 17, "18": 18, "19": 19}
fmt.Println(a)
fmt.Println(len(a))
删除某个 key
a := make(map[string]int, 5)
a["王"] = 12
fmt.Println(a)
delete(a, "王")
fmt.Println(a)
查看 key
for k, v := range a { // 同时查看 key ,value
fmt.Println(k, v)
}
for k := range a { // 只查看 key
fmt.Println(k)
}
前提:键不重复,可哈希
v1=make(map[int]int)
v2=make(map[string]int)
v3=make(map[[2]int]int)
v6=make(map[bool]int)
v4=make(map[[]int]int) // 不行,可以是数组,但不能是切片
v5=make(map[[2][]int]int) //会报错
v5=make(map[[2]map[string]string]int) //会报错
二.原理
1.1 hash的基本存储原理
用 拉模 + 拉链法
来快速了解 hash
表的存储原理
这是 Python 或者 go 中,对 map
这种存储类型的基本原理,但是每种语言中,针对自己的语言特点,都进行了进一步的优化。
1.2 Map的整体存储结构
其核心是由 hmap
和 bmap
两个结构体实现的。
1.2.1 初始化
// 初始化一个可容纳 10 个元素的map
info=make(map[string]string,10)
- 第一步:创建一个
hmap
结构体对象 - 第二步:生成一个哈希因子
hash0
并赋值到hmap
对象中(用于后续为 key 创建哈希值)。 - 第三步:根据
hint=10
,并根据算法规则来建立B
,当前B
应该为 1.
hint | B |
---|---|
0-8 | 0 |
9~13 | 1 |
14~26 | 2 |
… | … |
- 第四步:根据
B
去创建桶(bmap对象),并存放在buckets
数组中,当前的bmap
的数量应该为2
- 当
B<4
时,根据B
创建桶的个数的规则为:2B(标准桶) - 当
B>=4
时,根据B
创建桶的个数的规则为 :2B+2B-1 (标准桶 + 溢出桶) - 注意: 每个
bmap
中可以存储 8 个键值对,当不够存储时,需要使用溢出桶,并将当前bmap
中的overflow
字段指向溢出桶的位置。
- 当
1.2.2 写入数据
- 结合哈希因子和键
name
生成哈希值 - 获得哈希值的
后B位
,并决定该值放在哪个桶中 - 上一步确定以后,接下来就在桶中写入数据
获取哈希值得 tophash (哈希值的高八位) 将 tophash、key、value 分别写入桶的三个数组中,如果桶已经满了,则通过 voerflow 找到溢出桶,并在溢出桶中继续写入。
注意:以后在桶中查找数据时,会基于 tophash 来找(tophash相同则再去比较key)
- 第四步:
hmap
的个数count++
map中的元素+1
1.3 map 的扩容
map
中数据总个数/桶个数 > 6.5,引发翻倍扩容- 使用了太多的溢出桶 (溢出桶太多,会导致map处理速度降低)
B<=15
,已使用的溢出桶个数 >=2B,时,引发等量扩容B>15
,已使用的溢出桶的个数 >=215,引发等量扩容
1.4 迁移
- 如果是翻倍扩容的话,迁移规则就是将旧桶中的数据分流到两个桶中(比例不一样),并且桶编号的位置为:同编号位置 和 翻倍后对应的编号位置。
- 等量扩容的话,溢出桶太多引发的扩容,那么数据迁移机制就会比较简单,就是将旧桶中的值迁移到新桶中,这种迁移的目的就在于让数据更紧凑,从而减少溢出桶。