一、引言
如果您是Go 语言的新手,您可能会对如何在Go 中使用映射感到有点困惑。即使有经验,也可能很难理解映射的实际工作原理。
例如:
或者例如在映射中使用for-range循环时的排序问题。
在继续之前,请记住这里的信息—— 是基于Go 1.23 的。如果情况发生变化且此内容不再是最新内容,请私信我。
二、Go 语言中的映射:快速入门
1. 基本概念
Go 的Map 是用作键值存储的内置类型。与键递增索引的数组不同,映射的键可以是任何等效类型,从而赋予其更大的灵活性。
2. 如何创建
使用make() 创建一个空映射并分配它。 地图['a': 1,'b': 2]
键值对是在创建时通过映射文字直接设置的。3.删除操作
如果要删除键值对,可以使用删除函数delete(m, 'a')。
4. 零值和特殊情况
该map的零值为nil,相当于一个空map。搜索不存在的键将返回“零”作为值类型。但是,您无法将新的键值对添加到nil 映射中。
对于nil 映射遍历,不会引发错误,但不会执行操作。
5.其他特点
for-range 循环返回的键没有特定的顺序。映射不是线程安全的。您可以使用ok: _, ok :=m[key] 来检查该键是否在地图中。 6. 关于映射键
可比较类型:允许使用==比较相同类型的两个值的类型被视为可比较类型。但是,某些类型(例如切片、函数或包含切片和映射的结构)不能用作映射键。接口:接口可能具有可比性,也可能不具有可比性。使用空接口作为键时要小心,因为这可能会导致运行时错误。 运行时错误“hash of unhashable type []int”还揭示了有关Go 如何在幕后处理映射的线索。
三、映射(Map)剖析
1. 映射的基本结构
在Go 代码中,单个映射是一种抽象,隐藏了数据组织的复杂细节,由称为存储桶的单元组成。
映射包含一个指向桶数组的指针,以便在分配或传递映射时相关变量和函数参数可以共享相同的指针。
2. 传输和更改映射
注意,映射的底层指向hmap的指针,但它不是引用类型。对整个映射的更改不会影响原始映射。
在Go中,一切都是按值传递的。实际发生的情况有些不同。当你将map m1传递给changeMap函数时,Go会复制*hmap结构。所以main()中的m1和changeMap()函数中的m2在技术上是指向同一个hmap的不同指针。
映射是值类型
3、铲斗特点
每个桶最多可以容纳8 个键值对。
映射桶
上面的map有两个bucket,len(map)是6。
4. 简单的赋值场景
让我们看一下下图中的一个简单的分配场景。我有一个空地图,我想为其分配键值对'hello' : 1。
将键值对分配给空映射
首先将“hello”哈希为一个数字,然后对桶的数量取模。由于这里只有一个桶,所有模1的数字都是0,直接进入桶0。添加另一个键值对时会发生相同的过程,如果存储桶0 已满或键不同,我们将移动到下一个槽。
5. 哈希和循环中的关键顺序
14的顶端有4个铲斗,这就是有效载荷系数。还记得负载因子阈值是6.5 吗?它直接影响地图何时增长。
Tip 13 有两个桶,负载系数为13/2=6.5,达到但不超过阈值。如果将其增加到14,负载因子将超过6.5 并且将开始增长。
同样,如果有26个芯片和4个桶,则负载系数为26/4=6.5,再次达到阈值。超过26 个时,地图必须扩展以容纳更多元素。
基本上,从第二个范围开始,铲尖范围将比前一个范围增加一倍,并且铲斗的数量和容量将相似。
四、增长时的迁移
1. 增加映射带来的问题
绘制增长图回答了两个常见问题:
“为什么我无法获取映射元素的地址?”“为什么在不同点不能保证for-range 的顺序?”
当映射增长时,会分配一个新的桶数组,其大小是旧数组的两倍,因此旧桶中的条目位置无效,必须移动到新桶中。
映射迁移
如果您的映射有大量键值对(例如1000 个),则一次移动所有键可能会很昂贵并且会阻塞goroutine。为了避免这种情况,Go 使用“增量增长”来分散操作并保持程序平稳运行。
这个过程很复杂,需要管理新旧存储桶,同时保持映射完整性。
3. 渐进式增长的触发条件
只有两种情况会导致逐渐增长。
将键值对分配给映射。从地图上删除一个关键点。每个操作都会触发一个迁移过程,将至少一个存储桶移动到新的存储桶阵列。
当执行m['Hello']=2操作并且map增长时,包含'Hello'键的旧桶将首先被迁移。
键“Hello”可以移动到两个新存储桶之一
将旧桶元素移动到新桶的规则:
例如,从4个桶到8个桶,旧的“桶1”元素被移动到新的“桶1”或新的“桶5”。
Hash % 4==1 表示hash % 8==1 或hash % 8==5。因为在旧桶中,H % 4==1,所以H的最后两位是01。考虑新桶数组的最后3 位:
如果第三位(从右)是0,最后三位是001,则表示H % 8==1。如果第三位(从右起)是1,最后三位是010,这意味着H % 8==5。 如何迁移旧桶
如果旧桶有溢出桶,则将元素移动到新桶,所有元素都移动完毕后,旧桶被“top hash”字段标记为“已迁移”。
五、更多
掌握Golang精髓,释放你的编程潜力!关注我的专栏《Golang实用技巧》。您将发现生产中的最佳实践,并探索有关高并发编程的实用教程。从分享实用的Golang技巧到深入分析真实的应用场景,你可以成为真正的Golang高手。无论您是初学者还是经验丰富的开发人员,您都会找到所需的灵感和知识。让我们一起探索Golang的无限可能。
版权声明:本文转载于网络,版权归作者所有。如有侵权,请联系本站编辑删除。