Map

map底层是由哈希表实现的

Go使用链地址法来解决键冲突。

map本质上是一个指针,指向hmap

这里的buckets就是桶,bmap

每一个bucket最多可以放8个键值对,但是为了让内存排列更加紧凑,8个key放一起,8个value放一起。在8个key前面是8个tophash,每个tophash都是对应哈希值的高8位,最后由一个overflow字段指向下一个bmap,就是溢出桶。溢出桶内存布局和常规桶一样,bmap内存布局如图

如果哈希表要分配的桶的数目大于24,就会预分配2(B-4)个溢出桶备用。

这些溢出桶和常规桶在内存中是连续的,前2^B个用作常规桶,后面的用作溢出桶

查找过程
  1. 查找或者操作map时,首先key经过hash函数生成hash值
  2. 通过哈希值的低8位来判断当前数据属于哪个桶(bucket)
  3. 找到bucket以后,通过哈希值的高八位与bucket存储的高位哈希值循环比对
  4. 如果相同就比较刚才找到的底层数组的key值,如果key相同,取出value。
  5. 如果高八位hash值在此bucket没有,或者有,但是key不相同,就去链表中下一个溢出bucket中查找,直到查找到链表的末尾。
  6. 如果查找不到,也不会返回空值,而是返回相应类型的0值。
插入过程

新元素插入过程如下:

  1. 根据key值算出哈希值
  2. 取哈希值低位与hmap.B取模确定bucket位置
  3. 查找该key是否已经存在,如果存在则直接更新值
  4. 如果没找到将key,将key插入。
map扩容机制

为了保证访问效率,当新元素将要添加进map时,都会检查是否需要扩容,扩容实际上是以空间换时间的手段。

触发扩容的条件有二个:

  1. 负载因子 > 6.5时,也即平均每个bucket存储的键值对达到6.5个。

    负载因子 = 键数量/bucket数量
  2. 溢出桶(overflow)数量 > 2^15时,也即overflow数量超过32768时。

  • 增量扩容

​ 如果负载因子>6.5时,进行增量扩容。这时会新建一个桶(bucket),新的bucket长度是原来的2倍,然后旧桶数据搬迁到新桶。每个旧桶的键值对都会分流到两个新桶中

​ 考虑到如果map存储了数以亿计的key-value,一次性搬迁将会造成比较大的延时,Go选择“渐进式扩容”,先分配2倍内存空间的新桶,然后标记当前哈希表正在扩容,每次访问map时都会触发一次搬迁,这样可以把扩容消耗的时间分散到多次操作中。

  • 等量扩容

负载因子没超标,溢出桶较多

  1. ​ 如果常规桶数目不大于2^15,那么使用的溢出桶数目超过常规桶就算是多了;

  2. ​ 如果常规桶数目大于215,那么使用溢出桶数目一旦超过215就算多了。

    所谓等量扩容,就是创建和旧桶数目一样多的新桶,然后把原来的键值对迁移到新桶中,重新做一遍类似增量扩容的搬迁动作。这样做的目的是把松散的键值对重新排列一次,能够存储的更加紧凑,进而减少溢出桶的使用,以使bucket的使用率更高,进而保证更快的存取。

详细解读go语言中的map的更多相关文章

  1. 详细解读go语言中的chnanel

    Channel 底层数据结构 type hchan struct { qcount uint // 当前队列中剩余元素个数 dataqsiz uint // 环形队列长度,即可以存放的元素个数 buf ...

  2. 【翻译】go语言中的map实战

    业余时间翻译,水平很差,如有瑕疵,纯属无能. 原文链接 http://blog.golang.org/go-maps-in-action go语言中的map实战 1. 简介 哈希表是计算机科学中最重要 ...

  3. Go语言中的map(十一)

    map是一种无序的基于 key-value 的数据结构,Go语言中的map是引用类型,所以跟切片一样需要初始化才能使用. 定义map 定义 map 的语法如下: map[keyType]ValueTy ...

  4. Go语言中的map

    map是一个集合,可以使用类似处理数组和切片的方式迭代map中的元素.但map是无序的集合.无序的原因是map的实现使用了散列表. map的创建并初始化主要是两种方式: 1.内置的make函数 2.使 ...

  5. C语言中的static 详细分析

    转自:http://blog.csdn.net/keyeagle/article/details/6708077/ google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大 ...

  6. 【转载】C语言中的static 详细分析

    原blog地址:http://blog.csdn.net/keyeagle/article/details/6708077/ google了近三页的关于C语言中static的内容,发现可用的信息很少, ...

  7. [转]Go语言中的make和new

    前言 本文主要给大家介绍了Go语言中函数new与make的使用和区别,关于Go语言中new和make是内建的两个函数,主要用来创建分配类型内存.在我们定义生成变量的时候,可能会觉得有点迷惑,其实他们的 ...

  8. Attention is all you need 详细解读

    自从Attention机制在提出之后,加入Attention的Seq2Seq模型在各个任务上都有了提升,所以现在的seq2seq模型指的都是结合rnn和attention的模型.传统的基于RNN的Se ...

  9. 转:C语言中的static变量和C++静态数据成员(static member)

    转自:C语言中的static变量和C++静态数据成员(static member) C语言中static的变量:1).static局部变量        a.静态局部变量在函数内定义,生存期为整个程序 ...

随机推荐

  1. 7Java基础补充

    1.标准Java bean写法 包括:private修饰的成员变量.getter和setter以及无参和有多个参数的有参构造方法 2.String原理 String底层是字节数组byte[]. Str ...

  2. 【超值分享】为何写服务器程序需要自己管理内存,从改造std::string字符串操作说起。。。

    服务器程序为何要进行内存管理,管中窥豹,让我们从string字符串的操作说起...... new/delete是用于c++中的动态内存管理函数,而malloc/free在c++和c中都可以使用,本质上 ...

  3. Linux核心目录结构

    ------------恢复内容开始------------ 目录 含义及作用 /usr/bin 普通用户二进制命令目录 /usr/sbin root管理员用户使用的二进制命令目录 /boot 内核程 ...

  4. Dijkstra和堆优化

    Dijkstra算法 由于我之前一直记的迪杰斯特拉的翻译导致我把dijkstra写成了dijstra--所以下文#define dijstra dijkstra 我以后叫她迪杰克斯歘! Dijskra ...

  5. ifix中嵌入3d模型初探(一)

    在ifix项目中插入3d模型,是当前工控上位机的一个发展趋势,故而我也来尝尝鲜.利用现有条件,初步打算完成一个工厂俯视3d全景. 基本思路:利用webbrowser+3dmax+three.js来嵌入 ...

  6. K8S为什么要弃用Docker?Dockershim将移除

    一.背景由于最近知道了 K8s 新版本(v1.20)确定弃用 Docker 的消息,为了明确是否会对现有系统架构产生响,所以对涉及到的相关技术进行了一定的梳理(索性的是对现有的系统架构基本无影响:&g ...

  7. javascript 特殊字符 注意转义

  8. Java面向对象06——类与对象小结

    小结  /* 1. 类与对象    类是一个模板:抽象,对象是一个具体的实例 2. 方法    定义.调用 ​ 3. 对应的引用    引用类型: 基本类型(8)    对象是通过引用来操作的:栈-- ...

  9. IntelliJ IDEA 中为Maven 配置阿里云镜像源

    idea中查看位置,应该都是一样的这个位置 在此位置下新建一个.xml文件 内容如下: <?xml version="1.0" encoding="UTF-8&qu ...

  10. 最全总结 | 聊聊 Python 数据处理全家桶(PgSQL篇)

    1. 前言 大家好,我是安果! Python 数据处理全家桶,截止到现在,一共写过 6 篇文章,有兴趣的小伙伴可以去了解一下! 最全总结 | 聊聊 Python 数据处理全家桶(Mysql 篇) 最全 ...