再也不怕面试官拷打Go数据结构了!-Go语言map详解

news2024/11/25 13:16:22

map是Go语言中的哈希表,用来存储key/value键值对。可以在O(1)的时间复杂度内进行查找。map是Go语言在面试中经常会被问到的一个点,这篇文章就来细细梳理一下map的相关知识点,面试不再怕,offer顶呱呱。

哈希冲突

我们知道,哈希表是存储键值对的常用数据结构,而哈希表经常会遇到哈希冲突这一情况,指的是两个key/value键值对计算得出的hash值相同,会存储到同一个位置,就会发生撞车。解决方式有两种:拉链法或者开放寻址法。

  • 拉链法:将数组中的元素换成指针,数组中的每个元素指向一个链表。遇到哈希冲突的情况就将冲突的元素连接到链表后面。拉点发处理冲突简单,但是当链表长度过长时,可以采用优化策略,例如采用红黑树代替链表。
  • 开放地址法:简单来说就是当某个位置冲突时,就继续往后寻找空位,直到找到未使用的数据槽,将数据放入。

在Go语言中如何处理哈希冲突?小孩子才做选择,成年人当然全都要啦。

Go语言中map的底层结构

在Go语言中,map的底层结构是一个指向hmap的指针,占用8个字节。hmap中包含多个元素结构为bmap的bucket数组。bucket的底层采用链表将这些bmap连接起来。这里处理冲突用到了优化的拉链法,链表中的每个节点存储的不是一个键值对,而是8个键值对。
map的数据结构在源码结构中的关键字段如下,在src/runtime/map.go中

 type hmap struct {
     count     int     // 元素的个数
     flags     uint8   // 状态标志位,标记map的一些状态
     B         uint8   // buckets 数组的长度就是 2^B 个
     noverflow  uint16  // 溢出桶的数量 
     
     buckets    unsafe.Pointer  // 2^B个桶对应的数组指针,buckets数组的元素是bmap
     oldbuckets unsafe.Pointer  // 发生扩容时,记录扩容前的buckets数组指针
     
     extra *mapextra // 用于保存溢出桶的地址
 }
 
 type mapextra struct {
     overflow    *[]*bmap  // 溢出桶链表地址
     oldoverflow *[]*bmap  // 老的溢出桶链表地址
 
     nextOverflow *bmap  // 下一个空闲溢出桶地址
 }
 
 type bmap struct {
     tophash   [8]uint8  // 存储了bmap中8个k/v键值对的每个key根据哈希函数计算得出的hash值的高8位
     keys      [8]keytype // 存储了bmap中8个k/v键值对中的key
     values    [8]valuetype  // 存储了bmap中8个k/v键值对中的key
     overflow  uintptr // 指向溢出桶的指针
 }
 

在这里插入图片描述

map的访问原理

对map的访问两种方式:

v := map[key]     // 当map中没有对应的key时,会返回value对应类型的零值
v, ok := map[key] //当map中没有对应的key时,除了会返回value对应类型的零值,还会返回一个值存不存在的布尔值

map的访问步骤:

  1. 判断map是否为空或者无数据,若为空或者无数据返回对应的空值
  2. map写检测,若正处于写状态,表示此时不能进行操作,报fatal error
  3. 计算hash值和掩码
  4. 通过最后的B位确定在哪号桶。
  5. 根据key对应的前8位快速确定是在这个桶的哪个位置
  6. 对比key完成的hash是否匹配,匹配则获取对应value
  7. 如果都没有找到,就去连接的下一个溢出桶去找。

这里也可以看出,tophash的作用是快速确定key是否正确,也可以理解成一种缓存措施,如果前8位都不对,那后面也没必要比较了。
在这里插入图片描述

map的赋值原理

map的赋值操作很简单:

map[key] = value

原map中存在key时,则更新对应的值为value,若map中不存在key时,则插入键值对key/value。

注意

  • 对map赋值时,map一定先进行初始化。map初始化有:
m := map[key]value{}
m := make(map[key]value)
  • map是非线程安全的,不支持并发读写操作。当其他线程正在读写map时,执行map的赋值会报并发读写错误。
    **

map的赋值步骤:

**
map写检测,如果当前正处于写状态,表示此时不能进行赋值。
计算hash值,将map置为写状态
通过key的hash值后B位确定是哪一个桶
遍历当前桶,通过key的tophash和hash值,防止key重复,然后找到第一个可以插入的位置,即空位置存储数据。
如果当前桶元素已满,则会通过overflow连接创建一个新的桶。
再次判断map的写状态
清除map的写状态

map的扩容

map在两种情况下会触发扩容:

  • map的负载因子(当前map中,每个桶中的平均元素个数)已经超过6.5

正常情况下,如果没有溢出桶,每一个桶中最多有8个元素,当平均每个桶中的数据超过6.5个,那就意味着当前容量不足了,要扩容。

  • 溢出桶的数量过多

当B < 15时,如果overflow的bucket数量超过2^B
当B >= 15时,overflow的bucket数量超过2^15
简单来讲,新加入的key的hash后B位都一样,使得个别桶一直在插入新数据,进而导致它的溢出桶链条越来越长,如此一来,map在操作数据时就会变得很慢。及时扩容,可以重排数据,使元素在桶中的位置更加平均。
对应的,有两种不同的扩容策略:

  • 双倍扩容(负载因子 > 6.5)
  • 等量扩容(溢出桶过多)

双倍扩容

发生这种扩容是由于当前桶数组实在不够用了,这种扩容元素会重排,可能会发生桶迁移。

如图,扩容前B=2,扩容后B=3,假设一元素key的hash值后三位为101,那么由前文可知,扩容前,由hash的后两位决定几号桶,即01所以元素在1号桶。在扩容发生后,由hash的后三位来决定在几号桶,即101,所以元素会迁移到5号桶。

在这里插入图片描述

等量扩容

由于map中不断put和delete key,桶中可能会出现很多断断续续的空位,这些空位导致连接的bmap很长,导致扫描时间变长。这种扩容实际上是一种整理,把后置位的数据整理到前面,这种情况下,元素会重排,但不会换桶。
在这里插入图片描述

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

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

相关文章

【每日一题】74. 搜索二维矩阵

74. 搜索二维矩阵 - 力扣&#xff08;LeetCode&#xff09; 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非递减顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返…

探索 Java JDK 21 的新特性:一场深度解析之旅

探索 Java JDK 21 的新特性&#xff1a;一场深度解析之旅 探索 Java JDK 21 的新特性&#xff1a;一场深度解析之旅摘要 &#x1f680;引言 &#x1f310;正文 &#x1f4da;新特性概览 &#x1f195;1. 模式匹配的魅力 &#x1f9e9;2. 记录类型&#xff1a;轻松管理数据 &…

【深度学习】ONNX模型多线程快速部署【基础】

【深度学习】ONNX模型CPU多线程快速部署【基础】 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】ONNX模型CPU多线程快速部署【基础】前言搭建打包环境python多线程并发简单教程基本教程ONNX模型多线程并发 打包成可执行文件总结 前…

王道考研计算机组成原理

王道考研计算机组成原理 计算机系统概述计算机系统层次结构计算机的性能指标错题 数据的表示和运算数制与编码运算方法和运算电路浮点数的表示与运算 存储系统存储器概述主存储器主存储器与CPU的连接外部存储器高速缓冲存储器虚拟存储器 指令系统指令格式指令的寻址方式程序的机…

js加密双重保障之sm2国密

前言 ​ 最近看到一些项目里边有用到sm2/3/4国密加密算法&#xff0c;这里给大家简单介绍一下。 知识科普 SM2&#xff08;国密算法&#xff09;是一种非对称加密算法&#xff0c;由中国国家密码管理局&#xff08;NCC&#xff09;制定&#xff0c;并被广泛应用于中国的信息…

9月21日作业

登录代码&#xff1a; widget.h #ifndef REGISTER_H #define REGISTER_H#include <QWidget> #include <QDebug> #include <QSqlDatabase> #include <QSqlQuery> #include <QMessageBox>namespace Ui { class Register; }class Register : publ…

selenium自动化测试-获取动态页面小说

有的网站页面是动态加载的资源&#xff0c;使用bs4库只能获取静态页面内容&#xff0c;无法获取动态页面内容&#xff0c;通过selenium自动化测试工具可以获取动态页面内容。 参考之前的"bs4库爬取小说工具"文章代码&#xff0c;稍微修改下&#xff0c;就可以转成获…

9.21号作业

实现把注册的信息导入数据库中 second.h #ifndef SECOND_H #define SECOND_H#include <QWidget> #include <QDebug> namespace Ui { class Second; }class Second : public QWidget {Q_OBJECT public:void newslot(); public:explicit Second(QWidget *parent n…

机器学习西瓜书+南瓜书吃瓜教程学习笔记第四章决策树

1、算法原理 从逻辑角度&#xff0c;一堆if else语句的组合 从集合角度&#xff0c;根据某种准则划分特征空间 最终目的&#xff1a;将样本越分越“纯” 决策树是基于树结构来进行决策的 例如&#xff0c;我们对“这是好瓜吗&#xff1f;”这样的问题进行决策时&#xff0c;通…

QTC++ day12

注册登录界面 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QIcon> #include <QPushButton> #include <QLineEdit> #include <QLabel> #include <QDebug> #include <QMessageBox>//消息对话框类 #inc…

tracert 路由追踪原理

一、概念 就是利用ICMP(Internet Control Message Protocol)Internet控制报文协议 来追踪的计算机到目标计算机之间的所有路由器信息 二、不同平台下命令方式 windows下&#xff1a; tracert www.baidu.comlinux下 traceroute www.baidu.com 三、基于方式 UDP(user datagram pr…

【建议收藏】5个神级AI提示词网站,让AI工具效率拉满

HI&#xff0c;同学们&#xff0c;我是赤辰&#xff0c;本期是赤辰第9篇AI工具类教程&#xff0c;文章底部准备了粉丝福利&#xff0c;看完可以领取&#xff01; 今天给大家推荐五款非常好玩的AI提示词资源网站&#xff0c;完全免费&#xff0c;无需魔法&#xff0c;直接复制粘…

DIY 一个汽车方向盘游戏外设(MMOS OSW DIY)

OSW-MMOS直驱方向盘DIY过程记录 - 简书 (jianshu.com) DIY 一个汽车方向盘游戏外设&#xff08;MMOS OSW DIY&#xff09; 首先讲一下这个直驱系统大概的框架&#xff0c;首先是电脑&#xff0c;电脑里装MMOS的软件(这个软件国内高手把它汉化了的)&#xff0c;电脑通过USB线&a…

行业首款数字牙刷 F10 系列:笑容加带来的智能刷牙革命

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

为什么tomcat要自定义线程池实现?

背景 最近在研究tomcat调优的问题&#xff0c;开发人员做过的最多的tomcat调优想必就是线程池调优了&#xff0c;但是tomcat并没有使用jdk自己的线程池实现&#xff0c;而是自定了了线程池&#xff0c;自己实现了ThreadPoolExecutor类位于org.apache.tomcat.util.threads包下 …

数据湖存储在大模型中的应用

9月5日&#xff0c;浪潮信息新产品“互联网AIGC”行业巡展在深圳举行。本次巡展以“智算 开新局创新机”为主题&#xff0c;腾讯云存储受邀分享数据湖存储在大模型中的应用&#xff0c;并在展区对腾讯云存储解决方案进行了全面的展示&#xff0c;引来众多参会者围观。 ChatGPT…

A股20年数据回测结果mysql数据查询 phpadmin

编辑 数据库登录使用 1.登录mysql数据库管理台 phpadmin 访问地址&#xff1a; http://121.43.55.160:888/phpmyadmin_c77c64465f15a891/index.php 用户名&#xff1a;root 密码&#xff1a; root 2.切换到阿里云服务器 3 数据库密码 用户名&#xff1a;readonly 密码&am…

堆的OJ题

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ 小林的算法笔记       &#x1f6f0;️社区 :✈️ 进步学堂       &am…

【数据结构】二叉树的·深度优先遍历(前中后序遍历)and·广度优先(层序遍历)

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

2023最新Nmap安装和使用详解,超详细教程

【点击文章末尾卡片&#xff0c;先领资料再阅读哦~】&#x1f447; 文章目录 【点击文章末尾卡片&#xff0c;先领资料再阅读哦~】&#x1f447; Nmap概述功能概述运行方式 Nmap安装Nmap参数详解目标说明主机发现端口扫描端口说明和扫描顺序服务与版本探测脚本扫描操作系统探测…