golang种map底层是hashmap。非线程安全型map,不能并发读写,如果有多协程并发读写会发生fatal error,直接导致程序崩溃,无法被defer-recover捕获。
hashmap主要包含元素个数count,桶数组长度B(2^B),指向桶数组初始位置指针,预留的溢出桶
增加(修改)
map[key]=value
对于一个key,value对,需要先根据hash因子计算出hash值,在使用hash值对桶数组长度取模,得到桶位置。进行插入操作。
删除
delete(map,key)
对于一个key,value对,需要先根据hash因子计算出hash值,在使用hash值对桶数组长度取模,得到桶位置。先使用开放寻址法,根据高八位hash值查找位置,在找对对应的key进行比较,进行删除,删除后标记emptyOne,如果是最后一个位置标记emptyRest,如果下一个位置是最后一个位置,删除当前位置后,需要把当前位置标记emptyRest
查找
v,exist:=m[k]
遍历
for k,v:=range map{
}
hash冲突
通过拉链法和开放寻址法解决hash冲突。
一个桶中可以放8个key,value对,桶数组从前往后查找空闲位置存放数据,当8个位置放满后,通过拉出一个链表,继续存放数据。
开放寻址法:
需要使用连续的空间地址。
拉链法:
需要存储指针,有空间损耗。可以使用非连续地址空间,通过指针串联。
扩容
增量扩容(翻倍扩容)
如果总元素数/2'b>6.5,说明数据元素过多,需要通过翻倍扩容,生成一个新的桶数组,在增删改操作中,渐进式从老桶数组中将元素迁移到新数组,避免性能抖动。
等量扩容
如果溢出桶数量大于桶数组数量,说明可能因为删除操作导致桶中存在间隙,通过等量扩容,减少空隙。