简单易懂,解析Go语言中的Map

devtools/2025/2/23 4:52:24/

目录

  • 3. map
    • 3.1 初始化
    • 3.2 增删改查
    • 3.3 源码
    • 3.4 负载因子
    • 3.5 扩容

3. map

3.1 初始化

  • var/new声明nil map;make初始化map同时可以指定容量;字面量;
  • 向nil map中插入会报panic
go">func main() {var m1 map[int]int 		//panic: assignment to entry in nil mapm2 := *new(map[int]int)	// panic: assignment to entry in nil mapm3 := make(map[int]int,10)m4 := map[int]int{}m1[1] = 2m2[1] = 3m3[1] = 4m4[1] = 5
}

3.2 增删改查

基础增删改查如下

go">func main() {m4 := map[int]int{}m4[1] = 10			//增m4[1] = 20			//改delete(m4, 1)	    //删v, exist := m4[1]	//查if exist {fmt.Println(v)}
}
  • 若修改的时候,键不存在,则会新增
  • 可以对不存在的键进行删除,不会报错
  • 查询的时候,如果键不存在,返回:(零值,false)
go">func main() {m4 := map[int]int{}m4[2] = 20              //修改没有的键就是新增delete(m4, 0)		    //没有key:0的键也不会报错v, exist := m4[1]fmt.Println(v, exist)	//0 false
}
  • map并不是线程安全的,并发读写会触发panic
go">func main() {m4 := map[int]int{}go func() {for {m4[0] = 10}}()go func() {for {a, b := m4[0]   //fatal error: concurrent map read and map writefmt.Println(a, b) // 通常情况下应该是 0 false 但偶尔能在写的空窗期读到 10 true}}()go func() {for {m4[1] = 10      //fatal error: concurrent map writes}}()time.Sleep(2 * time.Second)
}

3.3 源码

  • 源码中的B,只影响buckets数组的长度,也就是bucket的个数,跟bucket内部能装多少个键值对无关
go">//map的数据结构
type hmap struct {count     int               // 元素个数B         uint8             // buckets数组大小buckets    unsafe.Pointer   // bucket数组,长度为2^boldbuckets unsafe.Pointer   // 旧bucket数组,用于扩容...
}

在bucket内的k-v超过8个时,会在创建一个新bucket,由overflow指向它 [扩容]

go">//bucket的数据结构
type bmap struct {tophash [bucketCnt]uint8    //存储Hash值得高8位data []byte                 //k-v数据,先存完k,再存voverflow uint8              //溢出bucket的位置
}

为什么要存Hash值的高8位?啥叫高8位,Hash低位在干什么?

打个比方,如果一个键k的hash值为113,我们一般会先对113%16(bucket数) = 1。好的,此时这个k就会被放入到buckets[1]中,也就是1号bucket

但是程序取模太慢了,为了加快运算速度,要是能把取模操作换成位运算就快多了

诶,在对2的N次方求余的时候,还真能够转化成位操作

eg:11%4 转化成二进制就是 1011 ;4 =2^2 ;将1011向左移动两位,得到10 就是2 也就是商 ,11 就是3 也就是余数

也就是说,如果一个数除以2的N次方求余,那么我们就是要得到最后这个数最后N位二进制的值

也就是 hash&(2^b-1)

通过上述公式得到的就是低位hash,也就是余数,用来确定桶。

但是这样只要低位相同,高位不同也在一个桶里,如11001111与11111111 低位都是1111,无法区别,此时就用到高位tophash来确定具体在桶中的位置了。

在Java中,为了增加散列程度,减少hash冲突,让bucket中的数据分布更加均匀,HashMap将高16位与低16位做异或运算,来确保每一位hash都参与到了桶运算中来

Go采取的是在hash函数中引入随机种子,来减少hash冲突,并使用高位来定位元素,也算是利用上了每一位hash

PS:为什么用异或?因为异或可以保证两个数值的特性,"&“运算使得结果向0靠近,”|"运算使得结果向1靠近

3.4 负载因子

负载因子 = k数/bucket数  //也就是计算出平均每个bucket包含多少k-v对

负载因子过低过高都不好,当负载因子大于6.5时,会进行rehash,增加桶数量并将这些hash均匀分布到其中

每个hash表对负载因子容忍能力不同,redis只能容忍负载因子为1(因为每个bucket只能存储1个k-v对)

3.5 扩容

触发扩容的两个条件,满足任一即可:

  • 负载因子大于6.5
  • overflow数量达到2^min(15,B) —— 溢出桶过多

扩容都是成倍数扩容,因为扩容本质上是B+=1;渐进式扩容,每次操作map的时候将2个bucket中的数据转移到新buckets中

扩容分为增量扩容和等量扩容:

  • 增量:桶不够用了,加桶
  • 等量:溢出桶太多了,有的都空了,重新排一下,减少一下溢出桶

如果处于扩容过程中,新增操作会直接在新buckets中进行, 但仍从oldbuckets开始寻找


http://www.ppmy.cn/devtools/161101.html

相关文章

Flutter CupertinoNavigationBar iOS 风格导航栏的组件

CupertinoNavigationBar 是 Flutter 中用于创建具有 iOS 风格导航栏的组件,它提供了类似 iOS 应用中导航栏的外观和交互效果。下面将详细介绍它的相关信息和具体用法。 特点 iOS 风格:具有 iOS 系统原生导航栏的外观和动画效果,包括标题样式…

精准测量PMD:OCI-V光矢量分析系统赋能光纤通信性能优化

在光纤通信技术飞速发展的今天,偏振模色散(PMD)已成为制约系统性能的核心瓶颈之一。PMD会导致信号失真、码间串扰,并限制传输距离,严重影响系统的带宽容量和传输可靠性。因此,精准测量PMD对于优化光纤通信系…

Qt的QTabWidget的使用

在PyQt5中,QTabWidget 是一个用于管理多个选项卡页面的容器控件。以下是其使用方法的详细说明和示例: 1. 基本用法 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QWidget, QLabel, QVBoxLayoutclass MainWindow(QMa…

数字信道化过程中多相滤波器组matlab代码及测试

数字信道化过程中多相滤波器组matlab代码及测试 列表 polyPhaseFilter/polyPhaseFilter.m , 1894 polyPhaseFilter/test_polyPhaseFilter.m , 792

基于eBPF的智能诊断平台:实现云原生系统的自愈型运维体系

引言:从被动运维到预测性自愈的进化 当某电商平台通过eBPF实时诊断系统提前48小时预测到MySQL集群的锁竞争风暴时,其核心是千万级指标粒度的内核状态分析与AI驱动的根因定位算法的结合。运维数据显示,该平台将平均故障恢复时间(M…

Spring Boot3.x集成Flowable7.x(一)Spring Boot集成与设计、部署、发起、完成简单流程

一、Flowable简介 Flowable 是一个轻量级、开源的业务流程管理(BPM)和工作流引擎,旨在帮助开发者和企业实现业务流程的自动化。它支持 BPMN 2.0 标准,适用于各种规模的企业和项目。Flowable 的核心功能包括流程定义、流程执行、任…

shell 脚本中的 sh 和 bash 是有区别的

shell 脚本中的 sh 和 bash 是有区别的 这两天在学习 shell 脚本相关知识,才知道 sh 和 bash 是不一样的。 bash 是 sh 的超集。bash 包含 sh。 比如 bash 中能用的 [[ ]] 和 数组 array("a" "b") 等,在 sh 中都不可用。 BASH 写…

238. 除自身以外数组的乘积(LeetCode 热题 100)

题目来源: 238. 除自身以外数组的乘积 - 力扣(LeetCode) 题目内容: 给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nu…