首页 > 其他分享 >go map

go map

时间:2023-07-15 10:24:15浏览次数:34  
标签:扩容 map 哈希 buckets bucket key go

map内存模型

// 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
}

B是buckets数组的长度的对数,buckets数组的长度就是2^B。
bmap就是桶,桶里面会最多装8个key。在桶内,根据key计算出来的hash值的高8位来决定key到底落入桶内的哪个位置(一个桶内最多有8个位置)。

bmap是存放k-v的地方。
每个bucket设计成最多只能放8个key-value对,如果有第9个key-value落入当前的 bucket,通过overflow指针连接新的bucket。

key定位过程

key经过哈希计算后得到哈希值,共64个bit位,计算它到底要落在哪个桶时,只会用到最后B个bit位。如果 B = 5,那么桶的数量,也就是 buckets 数组的长度是 2^5 = 32。

用哈希值的高8位,找到此key在bucket中的位置。先找到对应的桶,再去遍历bucket 中的key。

扩容

装载因子是key总数/桶的总数
loadFactor := count / (2^B)

插入新key时,进行条件检测,符合下面这2个条件,就会触发扩容。
装载因子超过阈值6.5(每个bucket有8个空位,在没有溢出,且所有的桶都装满了的情况下,装载因子算出来的结果是8。当装载因子超过6.5时,表明很多bucket都快要装满了,查找效率和插入效率都变低了。)——2倍扩容,新桶位置下标要么保持不变,要么加上2^B
overflow的bucket 数量过多:当桶总数小于2^15时,如果overflow的bucket数量超过桶总数;当桶总数大于等于2^15,如果overflow的bucket数量超过2^15。(第一种情况的补充,在装载因子比较小的情况下,查找和插入效率也很低,表面现象就是map里元素总数少,但是bucket数量多)——等量扩容

可以边遍历边删除吗

map不是一个线程安全的,同时读写一个map被检测到,会直接panic。可以通过读写锁来解决:sync.RWMutex。

key可以是float32或者float64型吗

从语法上看,是可以的。只要是可比较的类型都可以作为 key。除了slice,map,functions 这几种类型,其他类型都是OK的。任何类型都可以作为value,包括map类型。
当用float64作为key的时候,先转成unit64类型,再插入key中。2.4和 2.4000000000000000000000001经过math.Float64bits函数转换后的结果是一样的。二者在 map看来,就是同一个key了。
所以,虽然float32或者float64型可以作为 key,但是精度会带来其他问题。

sync.map很少使用的原因

在以下两个场景中使用sync.Map,会比使用map+RWMutex的方式,性能好得多:
只会增长的缓存系统中,一个key只写入一次而被读很多次;
多个goroutine为不相交的键集读、写和重写键值对。

总结

通过哈希查找表实现map,用链表法解决哈希冲突。
当向桶中添加了很多 key,造成元素过多,或者溢出桶太多,就会触发扩容。扩容分为等量扩容和2倍容量扩容。扩容后,原来一个bucket中的key一分为二,会被重新分配到两个桶中。
扩容过程是渐进的,主要是防止一次扩容需要搬迁的key数量过多,引发性能问题。触发扩容的时机是增加了新元素,bucket搬迁的时机则发生在赋值、删除期间,每次最多搬迁两个 bucket。

参考资料

年度最佳【golang】map详解

标签:扩容,map,哈希,buckets,bucket,key,go
From: https://www.cnblogs.com/WJQ2017/p/17555670.html

相关文章

  • patrickmn.gocache
    一句话概括基于内存的KV缓存,支持删除和过期以及持久化到文件并恢复。使用示例go.mod增加依赖requiregithub.com/patrickmn/go-cachev2.1.0+incompatiblepackagemainimport("log""time""github.com/patrickmn/go-cache")varc*cache.Cachefuncinit()......
  • 学的java,工作用的go?
    学的java,找的java开发,进了公司却在使用go。第一天让拉代码,我以为我拉的是java代码,没想到却是go。当时慌死了,我只听说过go,连helloworld都没有go写过。既来之,则安之,我接下来就是装goland,配环境变量,好在代码跑起来了,这个项目使用go+Gin来进行开发,甚至连数据库都不是我熟悉......
  • go context
    使用场景在协程之间传递上下文context接口typeContextinterface{//返回绑定当前context的任务取消的截止时间//如果没有设定期限,将返回ok==falseDeadline()(deadlinetime.Time,okbool)//绑定当前context的任务取消时返回一个关闭的channel......
  • R语言中 topGO包的安装
     001、if(!requireNamespace("BiocManager",quietly=TRUE))install.packages("BiocManager")BiocManager::install("topGO",force=TRUE)library(topGO)  。......
  • VSCode - Install/Update gotools
    View-->CommandPaletteInput'gotools'ClickOK.......
  • 说说 Go 语言的坑(二)
    上一篇文章说说Go语言for-range的坑说的是for-range的,工作中,其实还是遇到蛮多奇奇怪怪的问题,这里也顺便整理了一下,就当作是续集:)先继续看for-range的另一个坑:下面代码输出什么?funcmain(){ vara=[]int{1,2,3,4,5} varr=make([]int,0) fori,v:=ran......
  • 45.[1,2,3].mapparseInt答案是多少
    45.["1","2","3"].map(parseInt)答案是多少?parseInt()函数能解析一个字符串,并返回一个整数,需要两个参数(val,radix),其中radix表示要解析的数字的基数。(该值介于2~36之间,并且字符串中的数字不能大于radix才能正确返回数字结果值)。此处map传了3个参数(eleme......
  • go text模板
    packageinstallimport("bytes""fmt""strings""text/template""github.com/fanux/sealos/pkg/logger""sigs.k8s.io/yaml")varConfigTypestringfuncsetKubeadmAPI(versionstring){maj......
  • LeetCode 519. Random Flip Matrix 哈希Map
    Thereisanmxnbinarygridmatrixwithallthevaluesset0initially.Designanalgorithmtorandomlypickanindex(i,j)wherematrix[i][j]==0andflipsitto1.Alltheindices(i,j)wherematrix[i][j]==0shouldbeequallylikelytobereturne......
  • 用googletest写cpp单测
    框架概述GoogleTest(也称为googletest)是由Google开发的C++单元测试框架。它的首个版本是在2004年发布的,作为Google内部的测试框架使用。随后,GoogleTest在开源社区中得到广泛应用,并在许多项目和组织中成为首选的C++单元测试框架。GoogleTest提供了丰富的断言函数和......