了解 map 底层
我以前在写《深入理解 Go map:初始化和访问元素》时有介绍过 map 的基础数据结构。

基本结构如下图:


map 基本数据结构
其中重要的一个基本单位是 hmap:

type hmap struct {
 count     int
 flags     uint8
 B         uint8
 noverflow uint16
 hash0     uint32
 buckets    unsafe.Pointer
 oldbuckets unsafe.Pointer
 nevacuate  uintptr
 extra *mapextra
}
 
type mapextra struct {
 overflow    *[]*bmap
 oldoverflow *[]*bmap
 nextOverflow *bmap
}
count:map 的大小,也就是 len() 的值,代指 map 中的键值对个数。

flags:状态标识,主要是 goroutine 写入和扩容机制的相关状态控制。并发读写的判断条件之一就是该值。

B:桶,最大可容纳的元素数量,值为 负载因子(默认 6.5) * 2 ^ B,是 2 的指数。

noverflow:溢出桶的数量。

hash0:哈希因子。

buckets:保存当前桶数据的指针地址(指向一段连续的内存地址,主要存储键值对数据)。

oldbuckets,保存旧桶的指针地址。

nevacuate:迁移进度。

extra:原有 buckets 满载后,会发生扩容动作,在 Go 的机制中使用了增量扩容,如下为细项:

overflow 为 hmap.buckets (当前)溢出桶的指针地址。

oldoverflow 为 hmap.oldbuckets (旧)溢出桶的指针地址。

nextOverflow 为空闲溢出桶的指针地址。

我们关注到 hmap 的 B 字段,其值就是 6.5,他就是我们在苦苦寻找的 6.5,但他又是什么呢?

什么是负载因子
B 值,这里就涉及到一个概念:负载因子(load factor),用于衡量当前哈希表中空间占用率的核心指标,也就是每个 bucket 桶存储的平均元素个数。

另外负载因子与扩容、迁移等重新散列(rehash)行为有直接关系:

在程序运行时,会不断地进行插入、删除等,会导致 bucket 不均,内存利用率低,需要迁移。

在程序运行时,出现负载因子过大,需要做扩容,解决 bucket 过大的问题。

负载因子是哈希表中的一个重要指标,在各种版本的哈希表实现中都有类似的东西,主要目的是为了平衡 buckets 的存储空间大小和查找元素时的性能高低。

在接触各种哈希表时都可以关注一下,做不同的对比,看看各家的考量。

为什么是 6.5
了解是什么后,我们进一步深挖。

为什么 Go 语言中哈希表的负载因子是 6.5,为什么不是 8 ,也不是 1。这里面有可靠的数据支撑吗?

测试报告
实际上这是 Go 官方的经过认真的测试得出的数字,一起来看看官方的这份测试报告。

报告中共包含 4 个关键指标,如下:

loadFactor    %overflow    bytes/entry    hitprobe    missprobe
4.00    2.13    20.77    3.00    4.00
4.50    4.05    17.30    3.25    4.50
5.00    6.85    14.77    3.50    5.00
5.50    10.55    12.94    3.75    5.50
6.00    15.27    11.67    4.00    6.00
6.50    20.90    10.79    4.25    6.50
7.00    27.14    10.15    4.50    7.00
7.50    34.03    9.73    4.75    7.50
8.00    41.10    9.40    5.00    8.00
loadFactor:负载因子,也有叫装载因子。

%overflow:溢出率,有溢出 bukcet 的百分比。

bytes/entry:每对 key/elem 的开销字节数.

hitprobe:查找一个存在的 key 时,要查找的平均个数。

missprobe:查找一个不存在的 key 时,要查找的平均个数。

选择数值
结合测试报告一看,好家伙,不测不知道,一测吓一跳,有依据了。

Go 官方发现:负载因子太大了,会有很多溢出的桶。太小了,就会浪费很多空间(too large and we have lots of overflow buckets, too small and we waste a lot of space)。

来自 Go 官方源码说明
根据这份测试结果和讨论,Go 官方把 Go 中的 map 的负载因子硬编码为 6.5,这就是 6.5 的选择缘由。

这意味着在 Go 语言中,当 B(bucket)平均每个存储的元素大于或等于 6.5 时,就会触发扩容行为,这是作为我们用户对这个数值最近的接触。

总结
在今天这篇文章中,我们先快速了解了 Go 语言中 map 的基本数据结构和设计,这和我们要解释的问题紧密相关。

紧接着针对开头所提出的 6.5,进行了介绍和说明,这其实是 map 中的负载因子。其数值的确定来源于 Go 官方的测试。

为什么是 6.5,你懂了吗?

参考
src/runtime/map.go

深度解析golang map

golang中map底层B值的计算逻辑
————————————————
版权声明:本文为CSDN博主「煎鱼(EDDYCJY)」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/EDDYCJY/article/details/120359475

为什么 Go 的负载因子是 6.5?相关推荐

  1. HashMap负载因子

    下面是HashMap的一个构造函数,两个参数initialCapacity,loadFactor 这关系HashMap的迭代性能. 1 /** 2 * Constructs an empty < ...

  2. HashMap的负载因子为什么默认是0.75

    作用 负载因子是和扩容机制有关的,意思是如果当前容器的容量,达到了我们设定的最大值,就要开始执行扩容操作.比如说当前的容器容量是16,负载因子是0.75,16*0.75=12,也就是说,当容量达到了1 ...

  3. 什么是加载因子/负载因子/装载因子

    什么是加载因子(负载因子/装载因子)? 用于表示哈希表中元素填满的程度. 冲突的机会越大,则查找的成本越高.反之,查找的成本越低,从而查找的时间越少. HashMap中的加载因子 ①new HashM ...

  4. 高级数据结构与算法 | 哈希 :哈希冲突、负载因子、哈希函数、哈希表、哈希桶

    文章目录 哈希 哈希函数 常见的哈希函数 字符串哈希函数 哈希冲突 闭散列的解决方法 开散列的解决方法 负载因子以及增容 对于闭散列 对于开散列结构 具体实现 哈希表(闭散列) 插入 查找 删除 完整 ...

  5. 原创 | 我说我了解集合类,面试官竟然问我为啥HashMap的负载因子不设置成1!?...

    △Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 254篇原创分享 作者 l Hollis 来源 l Hollis(ID:hollischuang) 在Java基础中,集合 ...

  6. 我说我了解集合类,面试官竟然问我为啥HashMap的负载因子不设置成1!?

    在Java基础中,集合类是很关键的一块知识点,也是日常开发的时候经常会用到的.比如List.Map这些在代码中也是很常见的. 个人认为,关于HashMap的实现,JDK的工程师其实是做了很多优化的,要 ...

  7. 哈希表的基本概念详解以及具体实现(哈希函数、哈希冲突、负载因子)

    一.哈希表 哈希表是一个典型的用空间换时间的操作,利用数组随机访问的特性,最大化查找效率.哈希过程就是将数组元素与下标建立关系的过程. 二.哈希函数 1.哈希函数的意义: 哈希表是希望将元素与下标建立 ...

  8. HashMap的负载因子初始值为什么是0.75?这篇文章以最通俗的方式告诉你答案

    之前写过一篇专门介绍HashMap的文章,反响很不错,不过在留言区问的最多的问题就是HashMap的负载因子初始值为什么是0.75,私下又好好地研究了一番,总结了这篇文章. 本篇文章基于JDK1.8, ...

  9. HashMap中负载因子的意义是什么?

    学习记录 HashMap中负载因子的意义是什么? HashMap具有两个重要属性: size 和 load factor HashMap的实例具有两个影响其性能的参数:初始容量(0.75f)和负载因子 ...

  10. 哈希 :哈希冲突、负载因子、哈希函数、哈希表、哈希桶

    文章目录 哈希 哈希(散列)函数 常见的哈希函数 字符串哈希函数 哈希冲突 闭散列(开放地址法) 开散列(链地址法/拉链法) 负载因子以及增容 对于闭散列 对于开散列结构 具体实现 哈希表(闭散列) ...

最新文章

  1. VRPM包安装失败解决方案:had non-zero exit status
  2. javascript DOM 编程艺术----笔记
  3. 探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用
  4. 初学c++基础知识——第一个c++程序
  5. PR值:PagePank算法
  6. 娜璋旅行(三)九月的甘肃新疆:人间之边,天堂之界
  7. ASP.NET Core 设置允许跨域访问
  8. Redis高级特性介绍及实例分析
  9. 自己学java需要多久_自学 java, 学多久可以自己找到工作?
  10. mkv格式提取文件方法
  11. Java8中Map新方法:compute使用详解
  12. 机器学习下的持续交付
  13. 计算机信息系统的运行安全包括什么,计算机信息系统安全主要包括什么
  14. Spring核心功能之控制反转(IOC)
  15. H3C光模块专题笔记
  16. 齿轮箱常见故障数据_齿轮箱故障数据
  17. #{}ogl表达式_使用OGL制作程式化的鼠标轨迹
  18. 关于计算机语言最高奖项 图灵奖 的12位得主!你认识几个?
  19. 三种常见mq的优缺点比较
  20. 织梦DedeCms网站信息统计代码

热门文章

  1. python面试简历该怎么写?(附简历)
  2. 分享一套Android快速开发通用模板,包含常用主流框架,持续更新中……
  3. 在循环列表的富文本里摘出每个item的img标签内容(适合vue渲染)
  4. 图像融合常用数据集整理
  5. 用友黄颜色加密狗不稳定解决办法
  6. 计算机学校评分,中职学校计算机专业课堂教学评价
  7. js数组根据时间排序
  8. python3 zipfile_Python之zipfile模块的使用
  9. 手机版电台功能php源码,android音乐电台APP源码(客户端+服务端)
  10. Trello 便贴在线管理