map的实现原理
map的定义
map是由一组key,value键值对组成的抽象数据结构,并且同一个key
只会出现一次。
常见map操作
- add增加 k-v
- remove删除 k-v
- reassign修改 k-v
- lookup查询 k-v
增删改查的操作。
golang里的map结构
主要有两种数据结构
- 哈希查找表Hashtable
哈希查找表用一个哈希函数将key分配到不同的桶。
哈希查找表一般会存在碰撞问题,简而言之,不同的key被哈希分到了同一个桶里面。
一般有两种解决方式
- 链表法
链表顾名思义,讲桶bucket实现成一个链表,落在同一个桶中的key都会插入这个链表。 - 开放地址法
碰撞发生后,通过一定的规律,在数组的后面挑选空位,用来放置新的key
- 搜索树searchtree
- 平衡搜索树 AVL树,红黑树。
便利自平衡搜索树,返回的key序列,一般会按照大小从小到大的顺序,而哈希查找表是乱序的。
map的底层实现
go语言底层是采用链表法来解决hash冲突的。
type hmap struct {
count int
flags uint8
B uint8 // 是buckets数组的长度的对数,
// 也就是说Buckets的数组长度就是2^B
noverflow uint16
hash0 uint32
buckets unsafe.Pointer
oldbuckets unsafe.Pointer
nevacuate uintptr
extra *mapextra
}
bucket里面存储了key value
buckets 是一个指针
它指向一个结构体
typebmapstruct {
tophash[bucketCnt] uint8
}
typebmapstruct {
topbits [8]uint8
keys [8]keytype
value[8]valuetype
pad uintptr
overflow uintptr
}
这里的bmap就是桶
桶里面有8个key这些key之所以会落入同一个桶,是因为他们通过哈希计算后,哈希结果是一类的。
可以遍历的同时删除不
都知道map是一个不安全的数据结构。同时读写会被检测,直接Panic。多个线程的时候。
单个协程,就检测不到了。但是便利的结果可能不会是相同的。一般而言可以通过sync.RWMutex来解决。
可以对map内的元素取地址吗
无法对map的key的value 取地址
因为一旦发生扩容,之前的key,value值就会改变,之前的地址也就不一样了。
如何比较两个map
前提条件
- 都为nil
- 非空、长度相等、指向同一个map实体对象
- 相应的key指向的value深度相等
需要便利每个key value元素比较是否深度相等
为什么Key是无序的?
因为哈希存储是无需的底层数据结构的原因