go Map底层是怎么实现的

Go采用的是哈希查找表,使用链表解决哈希冲突来实现map

源码中map的结构体是hmap:

// A header for a Go map.
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 的两倍
	oldbuckets unsafe.Pointer
	// 指示扩容进度,小于此地址的 buckets 迁移完成
	nevacuate  uintptr
	extra *mapextra // optional fields
}

//buckets指向的结构体
type bmap struct {
	tophash [bucketCnt]uint8
}

在编译期间,bmap的结构会发生变化

type bmap struct {
    topbits  [8]uint8
    keys     [8]keytype
    values   [8]valuetype
    pad      uintptr
    overflow uintptr
}

bmap实际上就是bucket的内存结构,每个bucket中最多存8个key,实际上是时间和空间的权衡(如果每个只存一个,那么会占用很大的空间,如果每个可以存很多个,极端情况下查改时间复杂度变成O(n))

图片转载自https://www.cnblogs.com/qcrao-2018/p/10903807.html
图片转载自https://www.cnblogs.com/qcrao-2018/p/10903807.html

当map的key,value都不是指针并且size都小鱼128字节时会把bmap标记为不含指针,这时gc时就可以不用遍历整个hmap。bmap中其实有一个overflow的指针字段,这会使得bmap不满足不含指针,此时会把overflow移动到extra字段中

type mapextra struct {
	// overflow[0] contains overflow buckets for hmap.buckets.
	// overflow[1] contains overflow buckets for hmap.oldbuckets.
	overflow [2]*[]*bmap

	// nextOverflow 包含空闲的 overflow bucket,这是预分配的 bucket
	nextOverflow *bmap
}

bmap的内部组成
图片转自https://www.cnblogs.com/qcrao-2018/p/10903807.html
图片转自https://www.cnblogs.com/qcrao-2018/p/10903807.html

可以看到每个bmap中除了存储了key,value之外还有一个overflow指针。此外,可以看到key和value是各自放在一起的而不是key|value|key|value这样的,源码中说明这样的好处是某些情况下可以省略掉padding,节省内存空间。padding解释参考 在 Go 中恰到好处的内存对齐

一个bucket只能存放8个键值对,如果有更多的键值对被添加到当前的bucket,就会重新创建一个新的bucket,用overflow指针来连接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值