文章目录
- 环境
- 大纲
- 一、map基础知识
- map特点
- map初始化
- map访问
- 二、golang实现
- 1.数据结构
- 2. 访问
- 2.扩容(TODO)
- 总结(TODO)
- 参考资料
环境
go1.17.8 darwin/arm64
大纲
- map特点
- map基本用法
- hash table 相关知识
一、map基础知识
map特点
- map是哈希表的引用
- map需要初始化后使用
- 由于map是引用类型,所以传递的成本很低,64位机器上占8字节,32位机器上占4字节
- map的key的类型必须是可使用==和!=比较的
- map的中存储的元素是无序的
map初始化
- 使用字面量,初始化并赋值
m := map[int]string{
1:"one",
2:"two",
}
- 使用make关键字
m := make(map[int]string)
m[1]="one"
m[2]="two"
map访问
- 遍历
m := map[int]string{
1:"one",
2:"two",
}
for k,v := range m{
fmt.Println(k,v)
}
- 查找key对应的value
m := map[int]string{
1:"one",
2:"two",
}
v1 := m[1]
v2 := m[2]
如果map中对应的key不存在,则返回零值。或者判断key是否存在:
m := map[int]string{
1:"one",
2:"two",
}
if v1, ok := m[1]; !ok {
fmt.Println("key 1 is not exist")
}
- 删除key
m := map[int]string{
1:"one",
2:"two",
}
delete(m, 1)
二、golang实现
- go使用拉链法解决hash碰撞问题。
- 扩容时不会立马迁移和释放原有空间,而是通过后续的访问逐步的将旧空间中的内容迁移到新空间,再由GC释放旧空间。
1.数据结构
hmap相当于map指向hash表的头指针:
// A header for a Go map.
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
- count:map中的现有元素总数
- flags:缓存hash表的状态变量
- B:表示bucket的数量,bucket数量=2^B
- noverflow:计算溢出桶的数量
- hash0: hash函数的随机数种子,为hash函数添加随机性,map创建时确定
- buckets: 指向当前桶数组的第一个元素(桶数组的头指针)
- oldbuckets: 指向旧桶数组的第一个元素
- extra:指向溢出桶
// A bucket for a Go map.
type bmap struct {
tophash [bucketCnt]uint8
}
// compile internal struct
type bmap struct {
tophash [8]uint8
keys [8]keytype
values [8]valuetype
pad uintptr
overflow uintptr
}
- tophash:存储计算出的hash的高8位值
2. 访问
当一个键值对需要插入或者访问时,首先要根据key键找到存储键的桶,在从桶中查找出相应槽位的key和value。这就涉及到如下三个问题:
- 如何确定是哪个bucket
- tophash怎么确定
- 为什么key和value通过地址偏移量访问
golang中通过对key哈希后,取hash(key)的低B位来确定bucket的数组下标。通过hash(key)的高8位来与桶中槽位的tophash相比较从而确定应该存储或访问哪个槽位的数据。由于key和value是集中存储的(所有的key存一块儿,value存一块儿,有利于内存对齐),所以很容易通过地址的偏移量方位内存中相应的数据。图示如下:
2.扩容(TODO)
总结(TODO)
参考资料