map
map[key_type]value_type
map[string]string // key与value元素的类型相同
map[int]string // key与value元素的类型不同
删除
内建函数delete
增加/修改
map[key] = value
获取键值对数量
内建函数len(map)
遍历
for range
每次遍历的顺序不一致,k,v均可单独遍历
【知识碎片】
key类型必须支持==和 != 两种比较操作符,而函数类型、map类型,切片类型只支持nil,不支持同类型比较,所以不能作为map的key值
声明与初始化
// 声明
var m map[string]int
map["str"] = 1 //运行异常,必须初始化后再使用
// 初始化方式一:复合字面值
m := map[int]string{} // 有大括号 不等同于nil值,可以操作
m := mapmap[int][]string{
1:[]string{"1","2"},
2:{"2","3"}
}
// 初始化方式二:内建函数make
m := make(map[int]string) // 未指定初始容量
m := make(map[int]string, 6) // 指定初始容量为6
切片被赋予零值nil时,仍可以使用append函数,go中称为这叫零值可用,而map不行,如果不初始化 会导致运行时panic 若查找数据不存在,则会返回value元素类型的零值,使用 comma ok 查找数据,否则可能无法判断是否有改元素
comma ok
func main() {
// make(数组类型,长度,容量)
m := make(map[string]int)
_, ok := m["key1"]
if !ok {
fmt.Println("v is nil")
}
m["key1"] = 1
v1, ok := m["key1"]
if !ok {
fmt.Println("v is nil")
}
fmt.Println("v1: ", v1)
}
map 底层实现
Go 编译器会将 Go 语法层面的 map 操作,重写成运行时对应的函数调用。
【注】以下代码引用 《Tony Bai · Go 语言第一课》内容
// 创建map类型变量实例
m := make(map[keyType]valType, capacityhint) → m := runtime.makemap(maptype, capacityhint, m)
// 插入新键值对或给键重新赋值
m["key"] = "value" → v := runtime.mapassign(maptype, m, "key") v是用于后续存储value的空间的地址
// 获取某键的值
v := m["key"] → v := runtime.mapaccess1(maptype, m, "key")
v, ok := m["key"] → v, ok := runtime.mapaccess2(maptype, m, "key")
// 删除某键
delete(m, "key") → runtime.mapdelete(maptype, m, “key”)
运行时表示
type hmap struct {
// 元素个数,调用 len(map) 时,直接返回此值
count int
flags uint8
// buckets 的对数 log_2
B uint8
// overflow 的 bucket 近似数
noverflow uint16
// 计算 key 的哈希的时候会传入哈希函数
hash0 uint32
// 指向 buckets 数组,大小为 2^B
// 如果元素个数为0,就为 nil
buckets unsafe.Pointer
// 等量扩容的时候,buckets 长度和 oldbuckets 相等
// 双倍扩容的时候,buckets 长度会是 oldbuckets 的两倍
oldbuckets unsafe.Pointer
// 指示扩容进度,小于此地址的 buckets 迁移完成
nevacuate uintptr
extra *mapextra // optional fields
}
- map初始化后,在go runtime中会调用makemap函数 返回一个hmap类型的指针,hmap就是实际运行时的map。
- hmap.buckets 指向实际存数据的buckets[N]数据,buckets[i]有tophash[8]数组,8个key值,如果超出且未达到buckets扩容则末尾用overflow指针外接一个结构类似的bukets,成为overflow bukets。
map get过程
- 根据机器位数,如64获取一个64位的二进制数。
- 按hmap的B的值,如果B=5,则去上述64位数的后五位,定位buket[i]位置。
- 取64高8位作为top值,然后遍历buket[i]中tophash数组,该数组大小也为8,top = tophash[i]时,当前top值为地址,利用i 取地址得到key值,看是否等于map get传过来的key值,相等则利用i 获得value的地址,然后返回去地址后的值。