golang key map 所有_Map的底层实现 为什么遍历Map总是乱序的

Golang中Map的底层结构

其实提到Map,一般想到的底层实现就是哈希表,哈希表的结构主要是Hashcode + 数组。

  • 存储kv时,首先将k通过hashcode后对数组长度取余,决定需要放入的数组的index
  • 当数组对应的index已有元素时,此时产生一个【哈希冲突】。一般来说哈希冲突的解决方式为链表法,即在冲突的位置生成一个链表来存储元素

转自:https://www.jianshu.com/p/7aafee109f28

参考:go语言中文文档:www.topgoer.com

对于哈希表的具体实现和哈希冲突的解决方案这里就不赘述了,大家可以去看大神程序员小灰的漫画:什么是HashMap?。

Redis中的Dict结构(用于实现RedisDB、Hashmap,Set等)其实就是非常典型的哈希表。Golang中的Map结构与传统的哈希表稍有不同,它数组的基本单元并不是kv对,而是bucket。

每个bucket中可以放置8个kv对,这样可以减少对象的数量,有利于提升GC的性能;当bucket中放置不下时,会继续在溢出桶(overflow)中继续存储,串成一个链表的结构,如图所示:

a6b72ca5bf71d99b0f752614c6dc3bae.png

链表法解决哈希冲突

Bucket中结构详解

// map 哈希表type Hmap struct{         count int // 元素数量         ....         B uint8 // 哈希表数组的容量大小=2^B         ...         buckets unsafe.Pointer         oldBuckets unsafe.Pointer // 用于扩容         ...}// Bucket 桶type bmap struct{    tophash [8]uint8 // 哈希值的高8位    keys        values     overflow *bmap}
  1. key的哈希值的低B位(比如数组大小为32,2^5=32,则B=5)决定了它放入buckets中的index位置
  2. key的哈希值的高8位放入bucket中的tophash字段中,因为每个bucket中最多可以放8个KV对,所以tophash为大小为8的数组:tophash字段可以用于加速key的比较,当在一个bucket中查询某key时,可以先比较哈希值的高8位是否符合,如果符合才会比较对应的key,这样加速了key比较的过程;
  3. keys和values为分开存储的,因为key和value可能是不同类型,比如map[int64]int8,分开存储不需要进行字节补齐,可以节省空间;
  4. overflow即溢出桶,用于存储哈希冲突后超过8对的kv对

扩容操作

负载因子=元素数量/数组大小
redis是1时扩容;golang因为数组中元素为bucket,每个bucket可以放8个kv,所以是在load factor = 6.5时才会触发扩容逻辑:

  1. 扩容时容量翻倍,申请一个新的数组,采用渐进式哈希的方式进行迁移(和redis类似,可以避免影响性能)
  2. 迁移过程中支持读写:
  • 新增kv只写新表
  • 修改和删除双写,保证新老表中的数据一致
  • 读取时优先读老表,再读新表
  1. 迁移过程为每次update/remove操作时触发部分搬迁,每次搬迁2部分bucket中的数据:
  • 修改的key所在的当前Bucket
  • 按照顺序搬迁的一个Bucket(避免某些bucket一直未被访问导致无法搬迁成功)
  1. 直到所有数据搬迁完成后,删除oldBuckets,使得老哈希表中的Bucket对象被GC回收

遍历Map乱序之谜

在写代码时,当我们使用for k,v := range map {} 时会发现,每次输出的kv都是乱序的,既然map的底层是数组为什么不能按照固定顺序地输出呢?

结合上文我们说到扩容流程,由于扩容过程会新申请一个数组,并且将keys重新rehash后放入新的数组中。那么新的数组中的key的顺序就改变了,因此哈希表的底层实现使得map无法保证稳定地按照同一顺序输出keys。

Golang的作者为了”提醒“新手程序员不要依赖map遍历时返回的key顺序,采用随机选择遍历起始位置的方式使得遍历时返回是乱序的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388996.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

朴素贝叶斯分类器 文本分类_构建灾难响应的文本分类器

朴素贝叶斯分类器 文本分类背景 (Background) Following a disaster, typically you will get millions and millions of communications, either direct or via social media, right at the time when disaster response organizations have the least capacity to filter and…

第二轮冲次会议第六次

今天早上八点我们进行了站立会议 此次站立会议我们开了30分钟 参加会议的人员: 黄睿麒 侯熙磊 会议内容:我们今天讨论了如何分离界面,是在显示上进行限制从而达到不同引用展现不同便签信息,还是单独开一个界面从而实现显示不同界面…

markdown 链接跳转到标题_我是如何使用 Vim 高效率写 Markdown 的

本文仅适合于对vim有一定了解的人阅读,没有了解的人可以看看文中的视频我使用 neovim 代替 vim ,有些插件是 neovim 独占, neovim 和 vim 的区别请自行 google系统: Manjaro(Linux)前言之前我一直使用的是 vscode 和 typora 作为 markdown 编…

Seaborn:Python

Seaborn is a data visualization library built on top of matplotlib and closely integrated with pandas data structures in Python. Visualization is the central part of Seaborn which helps in exploration and understanding of data.Seaborn是建立在matplotlib之上…

福大软工 · 第十次作业 - 项目测评(团队)

写在前面 本次作业测试报告链接林燊大哥第一部分 调研,评测 一、评测 软件的bug,功能评测,黑箱测试 1.下载并使用,描述最简单直观的个人第一次上手体验 IOS端 UI界面简单明了,是我喜欢的极简风格。课程模块界面简洁优雅…

销货清单数据_2020年8月数据科学阅读清单

销货清单数据Note: I am not affiliated with any of the writers in this article. These are simply books and essays that I’m excited to share with you. There are no referrals or a cent going in my pocket from the authors or publishers mentioned. Reading is a…

c++运行不出结果_fastjson 不出网利用总结

点击蓝字 关注我们 声明 本文作者:flashine 本文字数:2382 阅读时长:20分钟 附件/链接:点击查看原文下载 声明:请勿用作违法用途,否则后果自负 本文属于WgpSec原创奖励计划,未经许可禁止转载 前言 之前做项目在内网测到了一个fastjson反序列化漏洞,使用dnslo…

FocusBI:租房分析可视化(PowerBI网址体验)

微信公众号:FocusBI关注可了解更多的商业智能、数据仓库、数据库开发、爬虫知识及沪深股市数据推送。问题或建议,请关注公众号发送消息留言;如果你觉得FocusBI对你有帮助,欢迎转发朋友圈或在文章末尾点赞[1] 《商业智能教程》pdf下载地址 …

米其林餐厅 盐之花_在世界范围内探索《米其林指南》

米其林餐厅 盐之花Among the culinary world, there are few greater accolades for a restaurant than being awarded a Michelin star (or three!), or being listed as one of the best in the world by a reputable guide. Foodies and fine dine lovers like myself, see …

差值平方和匹配_纯前端实现图片的模板匹配

基础介绍模板匹配是指在当前图像A里寻找与图像B最相似的部分,本文中将图像A称为模板图像,将图像B称为搜索匹配图像。引言:一般在Opencv里实现此种功能非常方便:直接调用result cv2.matchTemplate(templ, search, method)templ 为…

蓝牙耳机音量大解决办法_长时间使用蓝牙耳机的危害这么大?我们到底该选什么蓝牙耳机呢?...

蓝牙耳机避免了耳机线缠结,使人活动更自由,给人们带来了更加方便、舒适的听觉体验。但近日,英国《每日邮报》刊文表示,蓝牙耳机可能会危害人体健康。美国加州大学伯克利分校公共健康教授乔尔莫斯科维茨博士表示,已有研…

spotify 数据分析_我的Spotify流历史分析

spotify 数据分析Spotisis /spo-ti-sis/ noun The analysis of one’s Spotify streaming history using Python.Spotisis / spo-ti-sis / 名词使用Python分析一个人的Spotify流历史。 I was reading through a lot of data science related guides and project ideas when I …

intellig idea中jsp或html数据没有自动保存和更换字体

主题一:保存数据jsp intellig idea是自动保存数据的,看到没有保存 解决方案: 成功解决 主题二:更换字体: 或者快捷键CtelAlts 成功解决 转载于:https://www.cnblogs.com/weibanggang/p/9398498.html

java 环境变量

1.确保安装jrd jdk 2.环境变量配置 (1)新建->变量名"JAVA_HOME",变量值"C:\Java\jdk1.8.0_05"(JDK的安装路径) (2)编辑->变量名"Path",在原变量值的最后面加上“;%JAVA_HOME%\bin;%JAVA_HOME…

陆涛喜欢夏琳吗_夏琳·香布利斯(Charlene Chambliss):从心理学到自然语言处理和应用研究

陆涛喜欢夏琳吗技术系列中的女性 (WOMEN IN TECHNOLOGY SERIES) Interest in data science has been exponentially increasing over the past decade, and more and more people are working towards making a career switch into the field. In 2020, articles and YouTube v…

【angularJS】简介

简介 AngularJS 是一个 JavaScript 框架。它可通过 <script> 标签添加到 HTML 页面。 AngularJS 通过 指令 扩展了 HTML&#xff0c;且通过 表达式 绑定数据到 HTML。 AngularJS 是一个 JavaScript 框架。它是一个以 JavaScript 编写的库。 AngularJS 是以一个 JavaScrip…

纹个鸡儿天才小熊猫_给熊猫用户的5个提示

纹个鸡儿天才小熊猫A popular Python library used by those working with data is pandas, an easy and flexible data manipulation and analysis library. There are a myriad of awesome methods and functions in pandas, some of which are probably less well-known tha…

用户与用户组管理

linux最优秀的地方之一&#xff0c;就在于他的多用用户、多任务环境。 用户及用户组的概念 1、文件所有者 由于linux是一个多用户、多任务的系统。因此可能常常会有很多人同时使用这台主机来进行工作的情况发生&#xff0c;为了考虑每个人的隐私权以及每个人的喜好的工作环境&a…

代码 抠图_3 行 Python 代码 5 秒抠图的 AI 神器,根本无需 PS,附教程

曾几何时&#xff0c;「抠图」是一个难度系数想当高的活儿&#xff0c;但今天要介绍的这款神工具&#xff0c;只要 3 行代码 5 秒钟就可以完成高精度抠图&#xff0c;甚至都不用会代码&#xff0c;点两下鼠标就完成了。感受下这款抠图工具抠地有多精细&#xff1a;是不是很赞&a…

python函数使用易错举例

关于嵌套&#xff1a; 嵌套使用中&#xff0c; retrun inner ---> 返回的是函数的地址 retrun inner() &#xff1a; ---> 运行inner()函数 ---> 运行inner()函数后的返回值a&#xff08;假设&#xff09;返回上级 --> retrun inner()得到返回值a 如…