(三)Go Map 1.24之前是什么

一、Map是个什么东西

Map 是一种存储Key-Value的数据结构。底层依托数组 实现,通过哈希函数将 Key 转换为数组下标,从而将搜索复杂度降至 O(1)。

但是数组大小是有限的,数据是无限的,不同的 Key 难免会计算出相同的哈希值,产生哈希冲突。目前主流有两种解决方法

有两种解决方法:

  1. 链表法。存值的时候插入链表节点。这样插入,删除都很快。但是因为是链表,在内存离散分布,内存局部性利用率低。
  1. 开放寻址法。遇到冲突往n+1找。这样大多数据都在连续的数组空间内,内存局部利用率就高,但是也比链表扩容次数多。

现在的Go Map 1.24之后的实现已经转为了SwissTable。但是我认为去了解一下之前的实现,对于我们现在的新结构的理解是有帮助的。

二、Go Map 1.24之前的实现

Go 的 Map 底层由 2^n 个桶(Bucket)组成。每个桶内固定有 8 个槽位

为了追求极致的性能,桶内的布局并不是简单的 K-V, K-V... 循环,而是采取了分块存储:先排 8 个 tophash(哈希高位),再排 8 个 Key,最后排 8 个 Value。

  1. 这种设计核心目的有两个

一是利用 tophash 快速过滤无效匹配;二是利用 K/V 分离存储 来优化内存对齐,减少因类型对齐而产生的额外内存填充(Padding)。

  1. 讲讲为什么这里能够就是优化内存对齐。

首先如果是交替存储 [Key1][Value1][Key2][Value2]...

假设Key 是 int64(8字节),Value 是 bool(1字节)

由于int64要求必须从 8 的倍数地址开始存(对齐限制),为了保证下一个key2也能对齐,编译器会在value1(1字节)后面偷偷补上7 个字节的padding

三、Go Map 1.24之前的增删改查以及扩容

  1. 查找

首先利用哈希值的低 N 位确定桶(Bucket)的位置

进入桶后,利用哈希值的高 8 位(TopHash)与桶内的 8 个槽位进行快速比对。

如果 TopHash 匹配,再对比具体的 Key,从而精确定位 Value。

  1. 增加,修改,删除

首先按查找流程定位元素:

  • 若存在: 执行更新(修改)或删除操作。
  • 若不存在: 寻找空闲槽位进行插入。如果当前桶的 8 个槽位已满,则挂载并进入溢出桶(Overflow Bucket)继续寻找空位。
  1. 扩容

Go Map 的扩容分为两种触发条件:

  1. 双倍扩容: 当负载因子超过 6.5 时,说明数据太多,需翻倍扩容。
  2. 等量扩容: 当溢出桶过多(通常是由于频繁增删导致碎片化)时,为了整理内存进行等量扩容。

渐进式搬迁逻辑: 扩容并非一次性完成,而是通过 nevacuate 标志位记录搬迁进度。

写入: 直接操作新桶,并触发 1-2 个旧桶的搬迁。

查找/修改: 需对比 nevacuate。如果目标桶索引小于 nevacuate,说明已搬迁完成,去新桶 找;反之,则去旧桶找。"

相关推荐
我叫黑大帅5 小时前
Go 语言中处理「未知类型数据」的两大核心手段
后端·面试·go
小码哥_常6 小时前
从@RequestBody数据消失,到回调验签失败:一次棘手问题排查全记录
后端
小码哥_常6 小时前
Spring Boot 动态菜单权限系统:解锁企业级权限管理新姿势
后端
Java编程爱好者6 小时前
面试官:ConcurrentHashMap 为什么在 JDK 1.8 中废弃分段锁?
后端
SimonKing6 小时前
JetBrains+Qoder变身Agentic 编码平台,媲美Cursor、Trae等AI编程平台
java·后端·程序员
shark_chili6 小时前
Spring AI Alibaba深度实战:一文掌握智能体开发全流程
后端
摸鱼的春哥7 小时前
吃龙虾🦞咯!万字拆解OpenClaw的架构与设计
前端·javascript·后端
明月_清风7 小时前
Python 装饰器前传:如果不懂“闭包”,你只是在复刻代码
后端·python