如何实现get
- 分为两种:带 comma 和 不带 comma 就是带ok不带ok
package main
import "fmt"
func main() {
ageMap := make(map[string]int)
ageMap["qcrao"] = 18
// 不带 comma 用法
age1 := ageMap["stefno"]
fmt.Println(age1)
// 带 comma 用法
age2, ok := ageMap["stefno"]
fmt.Println(age2, ok)
}
遍历过程
复赋值过程
对 key 计算 hash 值,根据 hash 值按照之前的流程,找到要赋值的位置(可能是插入新 key,也可能是更新老 key),对相应位置进行赋值。
删除过程
扩容过程
-
Go 语言采用一个 bucket 里装载 8 个 key,定位到某个 bucket 后,还需要再定位到具体的 key,这实际上又用了时间换空间。
当然,这样做,要有一个度,不然所有的 key 都落在了同一个 bucket 里,直接退化成了链表,各种操作的效率直接降为 O(n),是不行的。
因此,需要有一个指标来衡量前面描述的情况,这就是
装载因子。Go 源码里这样定义装载因子:count / (2^B)count 就是 map 的元素个数,2^B 表示 bucket 数量。
-
扩容条件:装载因子超过阈值;overflow的bucket的数量过多(当 B 小于 15,也就是 bucket 总数 2^B 小于 2^15 时,如果 overflow 的 bucket 数量超过 2^B;当 B >= 15,也就是 bucket 总数 2^B 大于等于 2^15,如果 overflow 的 bucket 数量超过 2^15。)
3. 解决方法:a是房子太少,住户太多,那就就让B加一,新 bucket 只是最大数量变为原来最大数量(2^B)的 2 倍(2^B * 2)。
b.是房子太多人太少。解决办法就是开辟一个新 bucket 空间,将老 bucket 中的元素移动到新 bucket,使得同一个 bucket 中的 key 排列地更紧密。 变迁是一个渐进过程,不会一次完成。
key为什么无序?
-
扩容后key的位置发生改变
-
go规定遍历每次都是从随机bucket的水机cell开始遍历
float 型可以作为 key,但是由于精度的问题,会导致一些诡异的问题,慎用之。
可以边遍历边删除嘛?
-
多个协程同时读写同一个 map, map 并不是一个线程安全的数据结构。同时读写一个 map 是未定义的行为,如果被检测到,会直接 panic。
-
如果在同一个协程内边遍历边删除,并不会检测到同时读写,理论上是可以这样做的。但是,遍历的结果就可能不会是相同的了,有可能结果遍历结果集中包含了删除的 key,也有可能不包含,这取决于删除 key 的时间:是在遍历到 key 所在的 bucket 时刻前或者后。
不可以对map的key 和value取地址
俩map相等
map不是线程安全的。
在查找、赋值、遍历、删除的过程中都会检测写标志,一旦发现写标志置位(等于1),则直接 panic。赋值和删除函数在检测完写标志是复位之后,先将写标志位置位,才会进行之后的操作。