Golang Map 基本原理

2023-05-16

Go 语言中的 map 即哈希表。哈希表把元素分到多个桶里,每个桶里最多放8个元素。在访问元素时,首先用哈希算法根据 key 和哈希表种子获得哈希值(暂将其命名为 h),然后利用 h 的低 b b b 位得到桶的序号。其中桶的个数为 2 b 2^b 2b 个,是 2 的幂。桶中存储了所有元素的 key、value 和 key 哈希值的高 8 位。所以在找到桶之后会遍历元素的高 8 位哈希值,判断与 h 的高 8 位哈希值是否相等,若相等则再对比 key。如果在当前桶中没有找到 key,还会与溢出桶的元素进行比较。

在哈希表的数据结构中,结构体 hmap 是哈希表的核心,里面记录了一些元数据,例如元素数据、桶的数量、哈希表种子,还有用于储存数据的 buckets 数据。

type hmap struct {
/**元数据**/
  count int // 哈希表元素数量
  B int // 2^B=桶的个数 
  hash0 uint32 // 哈希表种子,在创建哈希表时确定
/**存储**/
  buckets []bmap // 桶,一个桶中最多有8个元素
  mapextra struct { // 溢出桶
    overflow *[]*bmap // 非预分配的溢出桶
    nextOverflow *bmap // 指向预创建的溢出桶 
  }
}

bmap 是桶的结构,里面存储了每个元素的 key、value 和哈希值的高 8 位。

type bmap struct {
    topbits [8]uint8 // 哈希值高 8 位
    keys [8]keytype // 存储key
    values [8]valuetype // 存储value
    overflow *bmap
}

哈希表的逻辑结构是正常桶和溢出桶组成链表,但其内存结构是正常桶与预创建的溢出桶在连续的内存空间中,其它溢出桶在需要时才会创建,内存不连续。

map 的逻辑结构

map 的创建过程:首先根据 make(map[keytype]valuetype, cap) 中传入的容量计算出桶的个数,计算规则是找出最大的 B B B 使得 cap > 6.5 ∗ 2 B \text{cap} > 6.5 * 2^B cap>6.52B,其中 6.5 是装载因子,表示平均一个桶里面放多少个元素。其中的 B B B 代表正常桶的个数,在创建 2 B 2^B 2B 个正常桶时,还要创建 2 B − 4 2^{B-4} 2B4 个溢出桶,因为可能会出现哈希函数产生了不均匀的哈希值,导致一个桶序号中包含的元素不止 8 个。

新增元素过程:如果新增的元素不存在于当前哈希表中,则把元素添加到正常桶中,如果正常桶满了,就尝试添加到溢出桶中,如果溢出桶也满了,则创建新的溢出桶。

扩容过程包含两步,首先创建一组新桶,然后迁移数据。新桶的大小由两个因素决定,如果装载因子超过 6.5,则容量翻倍,如果只是溢出桶太多,则容量不变。溢出桶多通常发生在向 map 中添加了很多元素后来又删掉的情况,容量不变的意义是将溢出桶中的数据“放回”正常桶中,不过不是放回原来的正常桶,而是放到新建的桶中。

在为新桶分配好内存后且在迁移数据之前会用 hmap.oldbuckets 指向旧桶。当有新的访问请求时优先访问旧桶,如果旧桶已迁移才会访问新桶。迁移数据的过程是惰性的,只有在 map 赋值或者删除时才会触发数据迁移,并且值迁移当前桶即对应的溢出桶,比如在 delete(map, key) 时计算出桶序号是 2,在旧桶大小为 4 的情况下,会把 2 号桶即其溢出桶根据哈希值复制到新 2 号和新 6 号桶中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Golang Map 基本原理 的相关文章

  • Apollo配置中心搭建

    目录 1 下载安装包和源码包2 创建数据库和表3 启动Apollo服务端4 访问Apollo客户端 1 下载安装包和源码包 下载地址 找到要安装的版本 xff0c 我这里选择的是1 3 0版本 下载好安装包后上传至linux的 usr lo
  • linux下搭建redis集群

    目录 1 准备三台服务器2 配置服务器3 安装redis4 配置集群5 测试集群 1 准备三台服务器 这是准备的三台服务器IP地址如下 xff0c 首先需要执行ping ip地址 xff0c 检查三台服务器之间是否能够相互通信 xff0c
  • linux下搭建redis哨兵

    1 准备三台Linux服务器 span class token variable 准备以下三台服务器 span span class token number 192 168 span span class token number 227
  • linux下安装elasticsearch

    目录 1 准备一台服务器2 下载elasticsearch安装包3 安装elasticsearch 1 准备一台服务器 这里使用的时redhat8 5 红帽新版的系统 xff0c 这里给的内存大小时4G 2 下载elasticsearch安
  • 用命令语句修改mysql某字段长度

    在MySQL中修改某个字段的长度 xff0c 需要使用ALTER TABLE语句 xff0c 具体操作如下 xff1a 假设要修改表A中的字段col1的长度为50 ALTER TABLE A MODIFY col1 VARCHAR 50 以
  • maven打普通包jar包(依赖一并打入)

    1 创建一个maven项目 这里可以看到新创建的maven项目 2 在pom xml添加项目需要的依赖 span class token generics span class token punctuation lt span depen
  • 纯Java项目批处理(打包方法二)

    1 创建一个普通的Java项目 主类 span class token keyword public span span class token keyword class span span class token class name
  • myCat搭建mysql的两主两从读写分离

    目录 前言1 搭建两主两从 xff08 主从复制 xff09 2 测试主从复制效果3 修改myCat配置文件4 启动myCat5 测试效果 前言 这里为了方便快速搭建我是使用docker容器完成的mysql实例 和使用在linux上安装的m
  • Red Hat Enterprise Linux9安装

    目录 前言1 启动iso镜像进入安装首页2 选择语言3 进入安装摘要界面4 选择安装目的地5 禁用KDUMP6 软件选择7 网络和主机名8 设置root用户密码9 开始安装10 重启11 登录系统12 挂载系统iso镜像13 配置本地镜像源
  • epel在线镜像源

    目录 前言阿里云镜像源腾讯镜像源华为镜像源清华大学镜像源中国科技大学镜像源 前言 此epel镜像源适用于红帽系列系统 xff0c 如Red Hat Enterprise CentOS AnoliOS Rocky Linux Almalinu
  • SUSE本地镜像源配置

    SUSE Enterprise Linux 11 12镜像源配置 mount dev sr0 mnt zypper ar file mnt local sles SUSE Enterprise Linux 15镜像源配置 先执行 mount
  • MyCat2搭建mysql主从分离

    前言 此次搭建MyCat2读写分离使用的Linux环境是Debian11 3 xff0c 使用的mysql版本是8 0 16 1 准备三台服务器 准备如下三台Debian服务器 xff0c 而且这三台服务器之间能够相互通信 xff0c 可以
  • Debian11安装redis报错解决方法

    1 报错需要安装C语言编译环境 执行apt install gcc 安装C语言编译环境 2 报错冲突 Conflicts 这里在执行apt install gcc命令后出现两gcc的版本 xff0c 这里执行apt install gcc
  • sqlserver在linux下安装

    前言 本次安装的sqlserver用的是sqlserver2017 linux环境是redhat Linux8 6 xff0c 本次采用是离线rpm安装 安装过程总体感觉比MySQL还要容易 xff0c 初步使用起来感觉和mysql差不多
  • DB2在Linux下静默安装

    目录 前言1 下载并上传db2压缩包到Linux2 检测db2安装环境3 安装db2数据库软件4 配置db2数据库系统用户5 创建数据库实例6 配置TCP IP通信服务7 配置数据库8 启动和关闭数据库实例9 修改权限10 数据库客户端和工
  • ubuntu12.04开启Framebuffer

    一 xff0e framebuffer概述 Framebuffer在Linux中是作为 设备 来实现的 xff0c 它是对图形硬件的一种抽象 xff0c 代表着显卡中的帧缓冲区 xff08 Framebuffer xff09 通过Frame
  • Redhat系列系统在线镜像源

    目录 前言Redhat7镜像源1 阿里云镜像源2 清华大学镜像源3 网易镜像源4 华为镜像源 Redhat8镜像源1 阿里云镜像源2 清华大学镜像源3 网易镜像源4 华为镜像源5 阿里云Rocky镜像源6 阿里云anolis镜像源 Redh
  • SuSE Enterprise linux安装mysql笔记

    目录 前言1 下载mysql二进制安装包2 解压MySQL安装包3 创建MySQL用户4 初始化mysql实例5 首次登录mysql6 修改登录密码 前言 本次安装MySQL的版本是8 0 30的二进制压缩包 xff0c 安装环境是SuSE
  • PostgresSql在linux下源码安装笔记

    目录 前言1 下载源码包并上传2 编译源码并安装3 本地登录PostgreSql4 客户端登录PostgreSql 前言 PostgreSql安装版本是14 5 xff0c 安装环境是Redhat Enterprise Linux serv
  • 判断两个IP地址(ipv4)是否在同一个网段

    我们通常会遇到的ip地址是这样的 xff1a ip地址 xff1a 192 168 227 205 子网掩码 xff1a 255 255 255 0 ip地址 xff1a 192 168 226 202 子网掩码 xff1a 255 255

随机推荐

  • 局域网搭建Linux镜像源

    前言 一般情况在企业的局域网内 xff0c 是不连接外网的 xff0c 所以像阿里云这样的在线的镜像源就用不了 xff0c 我相信大家个人在虚拟机里面连的就是阿里云镜像源了 xff0c 而且局域网内服务器较多的话 xff0c 本地挂载镜像源
  • ubuntu22.04 server安装

    目录 1 安装首页2 选择安装语言3 安装器4 选择键盘布局5 选择安装类型6 设置网络连接7 配置镜像源地址8 磁盘分区9 创建登录用户10 配置安装openssh server11 配置安装其他额外的软件12 开始安装系统13 重启系统
  • linux安装OceanBase数据库

    1 下载OceanBase数据库安装包 OceanBase官网下载页面 2 解压安装包并安装 tar xzf oceanbase all in one 4 0 0 0 beta 100120221102135736 el7 x86 64 t
  • linux下安装mysql客户端client

    1 下载mysql客户端 MySQL的Linux客户端官网下载地址 根据Linux的系统版本选择下载对应的rpm安装包 xff08 如下所示 xff09 xff0c 这里选择的是mysql8 0 27版本的redhat8系列的MySQL客户
  • linux下mysql的三种安装方法

    目录 1 离线安装 xff08 tar gz安装包 xff09 2 离线安装 xff08 rpm安装包 xff09 3 在线安装 xff08 yum安装 xff09 前言 安装环境 Redhat Enterprise Linux 8 1 离
  • linux+window+macos下的JDK安装

    1 Linux中安装JDK xff08 1 xff09 下载Linux版本的jdk压缩包 xff08 2 xff09 解压 tar zxvf 压缩包名 例如 xff1a tar zxvf jdk 8u251 linux x64 tar gz
  • bootstrap-table源码函数解读-sprintf

    var sprintf 61 function str var args 61 arguments flag 61 true i 61 1 str 61 str replace s g function var arg 61 args i
  • openGauss数据库的使用

    目录 前言1 启动 停止 重启数据库 xff08 1 xff09 极简版启动 停止 重启命令 xff08 2 xff09 企业版启动 停止 重启命令 2 登录数据库 xff08 1 xff09 登录数据库时的基本连接参数 xff08 2 x
  • openGauss数据库的安装(2.0.0极简版安装)

    目录 前言1 安装环境准备2 创建用户和用户组3 正式安装4 启动数据库实例并测试 前言 这里主要结合官网的文档 xff0c 安装系统环境是官网推荐的openEuler 20 03LTS openGauss数据库版本是openGauss 2
  • openGauss数据库安装(2.0.0企业版安装)

    目录 1 准备环境2 预安装3 正式安装4 启动并登录数据库 前言 此次数据库的系统安装环境仍然是openEuler20 03LTS openGauss安装版本是2 0 0版本 xff0c 相对于极简版安装 xff0c 确实多了一些工具 x
  • openEuler22.03安装

    目录 1 安装2 登录3 修改登录密码输错限制次数 1 安装 如果在此时没有设置网络 xff0c 那么需要在登录后可以编辑 etc sysconfig network scripts ifcfg ens160文件 xff0c 如下红框部分所
  • Linux查看日志常用命令

    第一种 xff1a 查看实时变化的日志 比较吃内存 最常用的 xff1a tail f app log 默认最后10行 xff0c 相当于增加参数 n 10 tail 200f app log 最后200行 xff0c 某一时刻往前推 Ct
  • ubuntu查看文件和文件夹大小

    在实际使用ubuntu时候 xff0c 经常要碰到需要查看文件以及文件夹大小的情况 有时候 xff0c 自己创建压缩文件 xff0c 可以使用 ls hl 查看文件大小 参数 h 表示Human Readable xff0c 使用GB MB
  • NLTK下载错误的终极解决办法

    Downloading package brown to C Users Ken AppData Roaming nltk data Error downloading 39 brown 39 from lt https raw githu
  • Tensorboard 不显示数据的问题

    No dashboards are active for the current data set Probable causes You haven 39 t written any data to your event files Te
  • Pytorch学习(2)——训练词向量的代码

    教程 xff1a https www bilibili com video BV1vz4y1R7Mm p 61 2 span class token keyword import span torch span class token ke
  • Windows 在不修改主题色的情况下将标题栏修改为黑色

    有些软件使用深色模式之后标题栏仍然是白色的 xff0c 很不美观 如果在 Windows 10 的设置中 xff0c 将个性化 颜色 在以下区域显示主题色 标题栏和窗口边框 选中 xff0c 那么标题栏可以带颜色 此时如果将主题色改为彩色
  • 解决debian(jessie)没有声音的问题

    先检查系统声卡驱动 lspci grep Audio 00 1b 0 Audio device Intel Corporation 82801I ICH9 Family HD Audio Controller rev 03 说明系统已经识别
  • 笔记类软件总结

    我大致把笔记类软件分为三类 xff1a 传统文档 思维导图 专业软件 1 传统文档 Typora 最经典的本地软件应该是 Typora 支持 Markdown 的实时预览 xff0c 界面简洁美观 使用基于 Chromium 浏览器的 El
  • Golang Map 基本原理

    Go 语言中的 map 即哈希表 哈希表把元素分到多个桶里 xff0c 每个桶里最多放8个元素 在访问元素时 xff0c 首先用哈希算法根据 key 和哈希表种子获得哈希值 暂将其命名为 h xff0c 然后利用 h 的低 b b b 位得