Golang之map tophash详解

tophash是一个长度为8的数组,它不仅仅用来存放key的哈希高8位,在不同场景下它还可以标记迁移状态,bucket是否为空等。弄懂tophash对我们深入了解map实现非常重要。
[外链图片转存失败(img-VCPzgZ2R-1564057589001)(./1560938034341.png)]
如上图:每一个tophash唯一对应一个K/V对。

tophash值

当tophash对应的K/V被使用时,存的是key的哈希值的高8位;当tophash对应的K/V未被使用时,存的是K/V对应位置的状态。

下面是map源码中对tophash状态值的定义。

@(src/runtime/map.go)

emptyRest      = 0 
emptyOne       = 1 
evacuatedX     = 2 
evacuatedY     = 3
evacuatedEmpty = 4
minTopHash     = 5 

当tophash[i] < 5时,表示存的是状态;
当tophash[i] >= 5时,表示存的是哈希值;

那么问题来了,如果key的哈希值高8位小于minTopHash时,这时候怎么区分是存的状态还是哈希值?

func tophash(hash uintptr) uint8 {
	top := uint8(hash >> (sys.PtrSize*8 - 8))
	if top < minTopHash {
		top += minTopHash
	}
	return top
}

直接看上面源码,第3-5行可以知道,当计算的哈希值小于minTopHash时,会直接在原有哈希值基础上加上minTopHash,确保哈希值一定大于minTopHash。

emptyRest

这个值有两层意思:一是表示该tophash对应的K/V位置是可用的;二是表示该位置后面的K/V位置都是可用的。

@(伪代码)

if tophash[i] == emptyRest {
	1 keys[i] is empty && values[i] is empty
	2 keys[i+1 .. N-1] is empty && values[i+1 .. N-1] is empty
} 

赋值:

  • 初始化的时,tophash会被置为emptyRest。
  • 删除map元素时,会判断是否需要把删除key对应的tophash置为emptyRest。

作用:

  • 判断bucket是否为空
    当tophash[0]==emptyRest表示整个bucket都是空的,这就是源码里面判断bucket是否为空的方法。

  • 查找时快速判断后面位置是否还需遍历
    如在查找时,在一个bucket中,找到tophash[2]位置,发现值为emptyRest,就可以判断该bucket没有该元素,继续查找下一个bucket。

emptyOne

仅表示该tophash对应的K/V位置是可用的,其后面的是否可用不知道。

赋值:
删除map元素时,会把key对应的tophash先置为emptyOne,再继续判断是否需要置为emptyRest。

evacuatedX && evacuatedY

这两个状态与扩容有关,记录元素被迁移到了新桶的部位–X或Y。如果是等位迁移,旧桶的元素必然被迁移到X部;如果是扩容迁移,旧桶元素可能迁移到X部,也可能迁移到Y部。当迁移到X部时,旧桶tophash置为evacuatedX;当迁移到Y部时,旧桶tophash置为evacuatedY。如下图:
[外链图片转存失败(img-SAnlVp1C-1564057589002)(./1560947852018.png)]

举个例子说明:扩容迁移,要把旧桶1的元素迁到新桶,因为新桶长度增长了一倍,因此旧桶1元素可能被迁移到新桶的1或5。当元素迁移到了1时,把旧桶tophash置为evacuatedX;反之,迁移到了5时,tophash置为evacuatedY。要注意置的是旧桶的tophash。

evacuatedEmpty

当bucket被迁移完时,tophash值置为evacuatedEmpty。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
嵌套map是指在Go编程语言中,使用map作为map的值。可以通过在map中存储map来实现嵌套map的功能。例如,可以创建一个类型为`map[string]map[string]string`的变量来表示一个嵌套map,其中外部map的键是字符串类型,值是内部map,内部map的键也是字符串类型,值是字符串类型。使用这种嵌套map的结构可以实现更复杂的数据存储和访问需求。 在golang中,可以通过以下方式来创建和操作嵌套map: ``` m := make(map[string]map[string]string) mm := make(map[string]string) mm["k1k1k1"] = "sssss" m["kkk"] = mm ``` 以上代码创建了一个嵌套map `m`,其中外部map的键是字符串类型,值是内部map `mm`。内部map `mm` 的键是字符串类型,值是字符串类型。可以通过`m["kkk"]["k1k1k1"]`来访问内部map中的值。 在上述代码的运行结果中,可以看到一个示例的嵌套map的输出结果。`MultityMapA`、`MultityMapB` 和 `MultityMapC` 是外部map的键,而对应的值是内部map。内部map的键是 `Key`,值是 `Value`。通过这种方式,可以在go中实现复杂的数据结构和逻辑。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [Golang 嵌套map赋值办法](https://blog.csdn.net/newjueqi/article/details/36380269)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [golang map多层嵌套使用及遍历方法汇总](https://blog.csdn.net/boyhandsome7/article/details/79734847)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值