golang中的map

本文探讨了Go语言中map的内部结构,包括count、B、hash0和buckets等组成部分。详细解释了tophash的作用以及溢出桶的概念,指出当哈希冲突严重时,会使用溢出桶来存储数据。此外,文章提到了map的两种扩容策略:等量扩容和翻倍扩容,并简单介绍了扩容的原因和目的。

         如图,golang中的map其实是个结构体,其中count是map中的数量,B是用来算有几个桶的,比如  a:=make(map[string]int,3) 中,B的值就是3,这时会map会准备2^3个桶。hash0是哈希种子,用来算哈希值的。buckets就是一个指向桶数组的指针,相同哈希值的值会放在同一个桶里,桶的源代码如图:

 这个tophash其实是用来存放八个哈希值的。

        所以map的结构差不多就是这样:

 那么这个overflow是什么呢?其实就是溢出桶,因为一个桶只能放8个数据,那么当哈希碰撞严重的时候,就只能挂个溢出桶了。溢出桶会在map建立时自动生成。

        当溢出桶太多时,会导致严重的性能下降,会进行map的扩容

 

map的扩容类型 有两种:

一:等量扩容:数据不多但是溢出桶多(比如之前的数据很多,但是删了很多的数据,导致溢出桶里的值很稀疏。所以我们需要等量扩容,其实就是 整理一下数据,使溢出桶里的值放在普通桶中。)

二:翻倍扩容:数据太多了 (这个也很好理解,就是溢出桶太多了,这时需要将普通桶变多)

        就大概介绍一下map的扩容,具体的扩容过程这边先不说。

### Golangmap 的底层实现原理 #### 数据结构概述 在 Golang 中,`map` 是一种内置的数据结构,其实现基于散列表(hash table)。这种设计使得 `map` 可以高效地完成键值对的增删改查操作。为了支持这一功能,Go 语言定义了两个核心结构体:`hmap` 和 `bmap`。 - **hmap (header for a Go map)** 这是 `map` 的头部结构,包含了描述整个 `map` 所需的各种元信息。其中最重要的字段之一是 `buckets`,这是一个指向 `bmap` 类型数组的指针[^4]。该字段用于定位实际存储键值对的桶(bucket)。 - **bmap (bucket for a Go map)** 每个 `bmap` 表示一个具体的桶,负责存储最多 8键值对。如果某个桶中的键值对数量超过上限,则会创建新的溢出桶(overflow bucket),并通过链表的形式链接到原始桶之后[^2]。 #### 键值对存储方式 在一个 `bmap` 中,键值对被分为以下几个部分进行存储: 1. **tophash 数组**: 记录每个键对应的高位哈希值片段(top hash bits),长度固定为 8。这些值用来快速判断某条记录是否存在以及是否需要进一步比较完整的键。 2. **keys 数组**: 存储所有的键对象。当发生冲突时(即多个不同的键映射到了同一个桶中),可以通过逐一匹配来找到目标键的位置。 3. **values 数组**: 对应于每一个键所关联的值。由于 key 和 value 总是一一对应的关系,所以它们可以分别保存在独立但平行排列的两组空间里。 以上三部分内容共同构成了单个 bmap 实例内部的实际布局情况;而通过 overflow 字段则能够扩展至更多连续分配出来的额外区域以便容纳超出初始容量限制之外的新成员项们[^4]。 #### 增删改查的操作机制 ##### 插入新元素 当向 map 添加一条全新的 k-v 组合时,程序首先计算给定 key 的 hash code 并据此决定应该放置在哪一号位置上的具体哪一个子分区当中去。假如发现那里已经有东西占据的话——无论是因为之前就存在完全相同的 entry 或者仅仅是单纯发生了碰撞现象而已——那么就会启动相应的解决策略来进行处理,比如寻找下一个可用 slot 或者建立 extra chain 结构等措施直至成功为止[^3]。 ##### 删除已有项目 要移除指定名称下的属性值很简单明了:只需简单地标记相应位点为空即可,并不需要做任何物理意义上的销毁动作除非特殊情况下才会触发真正的回收流程[^3]。 ##### 查询特定条件满足与否的结果判定逻辑说明如下所示伪代码形式表达出来便于理解掌握要点所在之处: ```go func lookup(h *hmap, key KeyType) Value { hash := computeHash(key) index := hash % len(h.buckets) // Start with primary bucket. var b *bmap = &h.buckets[index] loopOverBuckets(b): for i := range b.tophash { if isEmptyOrDeleted(b.tophash[i]) continue if matchesKey(hash, b.keys[i], key){ return b.values[i] } } if nextBucketExists(b.overflow){ b = getNextOverflowBucket() goto loopOverBuckets }else{ breakLoopAndReturnZeroValueForGivenKeyTypeAsDefaultAnswerAccordingToSpecsDefinedPreviouslyInThisParagraphAboveWhichStatesThatAccessingNonexistentElementsReturnsTheZeroValueOfTheirRespectiveTypesAutomaticallyWithoutRaisingErrorsExplicitlyTherebyEnsuringGracefulHandlingUnderAllCircumstancesRegardlessWhetherTheyExistWithinOurDataStructureAtRuntimeOrNotSoFarUntilNowAnywayButWhoKnowsWhatFutureHoldRight?! } } ``` #### 动态调整大小(扩容) 随着不断往里面塞进去越来越多的东西,原有的那些 preallocated slots 很可能很快就被填满了。此时就需要考虑如何优雅而又不失效率地应对这种情况的发生呢?答案便是所谓的 resizing mechanism 啦!它主要包括两种模式的选择依据具体情况灵活运用最佳实践方案达成目的效果最大化的同时兼顾性能表现最优解路径探索之旅开启啦朋友们准备好了吗让我们一起踏上这段奇妙旅程吧! 每当达到一定阈值比例关系的时候便会自动激活此过程从而确保整体运行状态始终保持健康稳定水平之上永不宕机崩溃哦亲~[^4] --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值