go语言Map详解
Map
Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现
map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。
它提供了高效的查找、插入和删除操作,非常适合用于构建关联数组或字典。
Map 的基本概念
每个 map 都是一个无序的集合,通过键来唯一标识值。
键可以是任何可以比较的类型(如字符串、整数、结构体等),而值可以是任何类型。
map 是引用类型,因此在传递给函数时,函数接收到的是指向同一底层数据结构的引用。
Go 中的 map 实现细节
Go 的 map 是基于哈希表实现的,具有高效的查找、插入和删除性能。
底层实现使用桶来存储多个键值对,并通过链表解决哈希冲突问题。
map 具有自动扩容的特性,以保持性能和存储效率。
哈希函数:每个键在插入时会被传入一个哈希函数,该函数会生成一个哈希值。这个哈希值用于确定在底层数组中的位置。
Go 使用的是非加密的哈希函数,针对不同类型(如字符串、整数等)具有不同的哈希算法。桶(Bucket):在 Go 的 map 中,底层数组被称为桶(bucket)。每个桶可以存储多个键值对,这解决了哈希冲突的问题。
当两个或多个键经过哈希运算后映射到同一个桶时,Go 会使用链表的方式将它们链接在该桶中。
每个桶的大小和数量会根据存储的键值对数量而动态扩展。扩容:当 map 中的元素数量超过一定阈值时,Go 会对 map 进行扩容。
扩容时,Go 将创建一个新的、更大的底层数组,并重新计算每个键的哈希值以确定新的位置,
然后将现有的键值对复制到新的桶中。
这种扩容策略旨在保持哈希表操作的效率,同时降低碰撞的概率。负载因子:负载因子是一个衡量哈希表是否需要扩展的指标,通常表示为元素数量与桶的数量之比。
在 Go 中,当负载因子超过某个阈值时,会触发扩容。
创建 Map
可以使用 make 函数或字面量来创建 map。
package mainimport "fmt"func main() {// 创建一个空的 map,键为字符串,值为整数ageMap := make(map[string]int)// 插入键值对ageMap["Alice"] = 25ageMap["Bob"] = 30fmt.Println("年龄Map:", ageMap) // 输出: 年龄Map: map[Alice:25 Bob:30]
}
package mainimport "fmt"func main() {// 使用字面量创建 mapfruits := map[string]int{"苹果": 10,"香蕉": 20,"橙子": 15,}fmt.Println("水果数量:", fruits) // 输出: 水果数量: map[香蕉:20 橙子:15 苹果:10]
}
访问和修改 Map
使用键来访问或修改对应的值。
package mainimport "fmt"func main() {fruits := map[string]int{"苹果": 10,"香蕉": 20,}// 访问值appleCount := fruits["苹果"]fmt.Println("苹果的数量:", appleCount) // 输出: 苹果的数量: 10// 修改值fruits["苹果"] = 12fmt.Println("更新后的苹果数量:", fruits["苹果"]) // 输出: 更新后的苹果数量: 12// 检查某个键是否存在if count, exists := fruits["橙子"]; exists {fmt.Println("橙子的数量:", count)} else {fmt.Println("橙子不存在")}
}
判断键是否存在
Go语言中有个判断map中键是否存在的特殊写法,格式如下
value, ok := map[key]
func main() {scoreMap := make(map[string]int)scoreMap["张三"] = 90scoreMap["小明"] = 100// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值v, ok := scoreMap["张三"]if ok {fmt.Println(v)} else {fmt.Println("查无此人")}
}
删除 Map 中的元素
可以使用 delete 函数删除某个键及其对应的值。
使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:
delete(map, key)
- map:表示要删除键值对的map
- key:表示要删除的键值对的键
如果删除的键不存在,则delete()函数不会报错。
package mainimport "fmt"func main() {fruits := map[string]int{"苹果": 10,"香蕉": 20,}// 删除香蕉delete(fruits, "香蕉")fmt.Println("删除后的水果数量:", fruits) // 输出: 删除后的水果数量: map[苹果:10]
}
遍历 Map
可以使用 for range 循环遍历 map 的所有键值对。
遍历map时的元素顺序与添加键值对的顺序无关。
package mainimport "fmt"func main() {fruits := map[string]int{"苹果": 10,"香蕉": 20,"橙子": 15,}// 遍历 mapfor fruit, count := range fruits {fmt.Printf("%s 的数量是: %d\n", fruit, count)}
}//只遍历key
for key := range fruits {fmt.Println(key)
}//只遍历value
for _, value := range fruits {fmt.Println(value)
}
按照指定顺序遍历map
- 将 map 中的键提取到切片中。
- 对切片进行排序。
- 按照排序后的键顺序遍历 map。
逻辑步骤概述
提取键:我们使用 for key := range fruits 迭代 map 的键,并将这些键添加到切片 keys 中。
排序:使用 sort.Strings(keys) 对键进行排序。根据具体的键类型(如 int、string、float 等),可以使用适当的排序函数。
遍历:最后,我们遍历排序后的切片,并使用它来访问 map 中的值,按照特定顺序输出结果。
package mainimport ("fmt""sort"
)func main() {// 创建一个 mapfruits := map[string]int{"香蕉": 20,"苹果": 10,"橙子": 15,"草莓": 25,}// 1. 提取键到切片keys := make([]string, 0, len(fruits))for key := range fruits {keys = append(keys, key)}// 2. 对切片进行排序sort.Strings(keys)//调用sort.Strings()函数对切片进行排序,默认升序排序// 3. 按照排序后的键顺序遍历 mapfor _, key := range keys {fmt.Printf("%s 的数量是: %d\n", key, fruits[key])}
}苹果 的数量是: 10
香蕉 的数量是: 20
草莓 的数量是: 25
橙子 的数量是: 15
元素为map类型的切片
在 Go 语言中,切片的元素可以是任意类型,包括 map 类型。这种灵活性使得可以轻松地组织和管理复杂的数据结构。
当需要存储一组具有相同特征的物体时,可以使用 map 来表示每个物体的属性,然后将这些 map 存储在切片中。
逻辑步骤概述
创建切片:我们定义了一个切片 studentGrades,其元素类型为 map[string]int,用来存储学生的姓名和成绩。添加元素:使用 append() 函数将新的 map 添加到切片中,每个 map 存储一个学生的成绩。遍历切片:通过两层 for 循环,遍历切片中的每个 map,并输出学生的姓名和成绩。
package mainimport "fmt"func main() {// 创建一个切片,元素类型为 map[string]intvar studentGrades []map[string]int// 添加学生的成绩到切片中studentGrades = append(studentGrades, map[string]int{"Alice": 85,"Bob": 90,})studentGrades = append(studentGrades, map[string]int{"Charlie": 70,"David": 95,})// 遍历切片中的 mapfor i, grades := range studentGrades {fmt.Printf("学生 %d 的成绩:\n", i+1)for name, grade := range grades {fmt.Printf(" %s: %d\n", name, grade)}}
}学生 1 的成绩:Alice: 85Bob: 90
学生 2 的成绩:Charlie: 70David: 95
可以根据需要使用不同类型的 map,例如:map[string]string:用于存储键为字符串,值也为字符串的映射关系,如存储用户信息或配置项。嵌套结构:可以将更复杂的结构体作为值。type Student struct {Name stringGrade int
}var students []map[string]Studentstudents = append(students, map[string]Student{"Alice": {Name: "Alice", Grade: 85},"Bob": {Name: "Bob", Grade: 90},})
值为切片类型的map
在 Go 语言中,您可以创建一个 map,其值类型为切片(slice)。
这种数据结构非常适用于需要将多个值与某个键关联的场景,
在 Go 语言中,使用值为切片类型的 map 允许您轻松管理多个值与对应键的关系。
这种灵活性使得可以满足复杂的数据结构需求,便捷地组织和访问数据。
在实际开发中,可用于各种场景,例如学生管理、产品信息处理等。
比如将多个学生的成绩与他们的名字关联,或是将多个订单的项目与一个用户关联。
逻辑步骤概述
创建 map:初始化一个 map,键为 string,值为 []int(整型切片)。添加数据:向 map 中添加键值对,其中值是一个切片,存储多个成绩。遍历 map:通过 for range 遍历 map,并对每个键值的切片进行进一步遍历,输出学生的姓名及其成绩。更新成绩:使用 append() 函数向某个学生的成绩切片中添加新的成绩,实现动态更新。
package mainimport "fmt"func main() {// 创建一个 map,键为字符串,值为整数切片scores := make(map[string][]int)// 向 map 中添加数据scores["Alice"] = []int{85, 90, 92}scores["Bob"] = []int{78, 82}scores["Charlie"] = []int{88, 91, 85}// 遍历 mapfor name, scoreList := range scores {fmt.Printf("%s 的成绩: ", name)for _, score := range scoreList {fmt.Printf("%d ", score)}fmt.Println() // 换行}// 示例:给某个学生添加一个成绩scores["Alice"] = append(scores["Alice"], 95)fmt.Printf("更新后 Alice 的成绩: %v\n", scores["Alice"])
}
Alice 的成绩: 85 90 92
Bob 的成绩: 78 82
Charlie 的成绩: 88 91 85
更新后 Alice 的成绩: [85 90 92 95]
其他用法
可以根据需要将值的类型定义为其他类型的切片,
如 map[string][]string 用于存储学生的课程列表,
或 map[string][]float64 用于存储某个产品的价格历史等。
courses := make(map[string][]string)
courses["Alice"] = []string{"数学", "英语", "科学"}
courses["Bob"] = []string{"艺术", "体育"}for student, courseList := range courses {fmt.Printf("%s 的课程: %v\n", student, courseList)
}prices := make(map[string][]float64)
prices["苹果"] = []float64{1.5, 2.0, 2.5}
prices["香蕉"] = []float64{1.8, 2.2, 2.6}for fruit, priceList := range prices {fmt.Printf("%s 的价格: ", fruit)for _, price := range priceList {fmt.Printf("%.2f ", price)}fmt.Println() // 换行
}
注意事项
无序性:map 的元素顺序是无序的。在遍历时,输出的顺序并不一定与插入的顺序相同。
引用类型:map 是引用类型,传递给函数时传递的是引用,因此对 map 的修改会影响到原始数据。
零值:如果尝试访问一个不存在的键,Go 会返回该值类型的零值。在整型 map 中,未存在的键返回 0。
并发安全:map 在并发环境下不安全,多个 goroutine 同时读写 map 可能会导致程序崩溃。可使用 sync.Mutex 或其他机制来实现并发安全访问。
相关文章:
go语言Map详解
Map Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现 map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。 它提供了高效的查找、插入和删除操作,非常适…...
C++——已知数组a[6]={1,3,5,7,9};输入一个数值,要求按照现有排序规律将它放入数组当中。
没注释的源代码 #include <iostream> using namespace std; int main() { int a[6]{1,3,5,7,9}; int n,i,j; cout<<"请输入一个数值:"; cin>>n; for(int i0;i<4;i) { if(n<a[i]) { …...
云计算第四阶段---CLOUD Day7---Day8
CLOUD 07 一、Dockerfile详细解析 指令说明FROM指定基础镜像(唯一)RUN在容器内执行命令,可以写多条ADD把文件拷贝到容器内,如果文件是 tar.xx 格式,会自动解压COPY把文件拷贝到容器内,不会自动解压ENV设置…...
深入解析ThingsBoard与ThingsKit物联网平台的差异
VS 在物联网(IoT)领域,平台的选择对于企业来说至关重要。本文将深入探讨ThingsBoard社区版与ThingsKit企业版这两个物联网平台的差异,帮助读者更好地理解它们的特色和适用场景。 系统相同点 首先,ThingsBoard社区版和ThingsKit企业版都基于…...
五、CAN总线
目录 一、基础知识 1、can介绍 2、CAN硬件电路 3、CAN电平标准 4、CAN收发器芯片介绍 5、CAN帧格式 ① CAN帧种类 ② CAN数据帧 ③ CAN遥控帧编辑 ④ 位填充 ⑤ 波形实例 6、接收方数据采样 ① 接收方数据采样遇到的问题 ② 位时序 ③ 硬同步 ④ 再同步 ⑤ 波…...
Linux:终端(terminal)与终端管理器(agetty)
终端的设备文件 打开/dev目录可以发现其中有许多字符设备文件,例如对于我的RedHat操作系统,拥有tty0到tty59,它们是操作系统提供的终端设备。对于tty1-tty12使用ctrlaltF*可以进行快捷切换,下面的命令可以进行通用切换。 sudo ch…...
钉钉与MySQL对接集成获取部门列表2.0打通EXECUTE语句
钉钉与MySQL对接集成获取部门列表2.0打通EXECUTE语句 接入系统:钉钉 钉钉是阿里巴巴集团打造的企业级智能移动办公平台,是数字经济时代的企业组织协同办公和应用开发平台。钉钉将IM即时沟通、钉钉文档、钉闪会、钉盘、Teambition、OA审批、智能人事、钉工…...
微信小程序点赞动画特效实现
这里提供两种实现点赞动画特效的方法: 方法一:使用 CSS 动画 wxml 文件: <view class"like-container"><image src"{{isLiked ? likedImg : unlikedImg}}" class"like-icon {{isLiked ? liked : }}" bindta…...
Day25笔记-普通文件读写with上下文二进制文件csv文件
一、文件读写【重点掌握】 常见文件的读写分类: 1.普通文件文件,如txt,py,html等 2.二进制文件,如图片,音频,视频,压缩包等 3.csv文件,如csv,需要借助于系统模块csv 4.对…...
MySQL安装教程
MySQL安装教程 如果需要删除原有mysql,然后安装过新的,可以参照如何彻底卸载旧mysql重装测试 1. 准备资源 mysql官网直达:https://dev.mysql.com/downloads/mysql/ CADN:https://download.csdn.net/download/luocong321/89592962 …...
【Windows】快速帮你解决如何找到 Windows 上的 .condarc 文件
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 专栏介绍 在软件开发和日常使用中,BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…...
『正版软件』XYplorer 专业的 Windows 文件管理工具软件
在数字化时代,我们每天都在与各种文件打交道。无论是工作文档、个人照片还是多媒体资料,管理这些文件的效率直接关系到我们的工作效率和生活体验。今天,我要向大家推荐一款功能强大、操作简便的文件管理软件 —— XYplorer。 XYplorer&#x…...
“吉林一号”宽幅02B系列卫星
离轴四反光学成像系统 1.光学系统参数: 焦距:77.5mm; F/#:7.4; 视场:≥56゜; 光谱范围:400nm~1000nm。 2.说明: 光学系统采用离轴全反射式结构,整…...
我的AI工具箱Tauri版-FasterWhisper音频转文本
本教程基于自研的AI工具箱Tauri版进行FasterWhisper音频转文本服务。 FasterWhisper音频转文本服务 是自研AI工具箱Tauri版中的一款模块,专门用于将音频或视频中的语音内容自动转化为文本或字幕。通过简单的配置,该工具能够批量处理大量音频或视频文件&…...
Java后端中的延迟队列实现:使用Redis与RabbitMQ的不同策略
Java后端中的延迟队列实现:使用Redis与RabbitMQ的不同策略 大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿! 在后端开发中,延迟队列(Delayed Queue)…...
Linux中使用cp命令的 -f 选项,但还是提醒覆盖的问题
问题: linux 在执行cp的命令的时候,就算是执行 cp -f 也还是会提醒是否要进行替换。 问题原因: 查看别名,alias命令,看到cp的别名为cp -i,那就是说cp本身就是自带覆盖提醒,就算我们加上-f 的…...
互联网技术的持续演进:从现在到未来
互联网技术的持续演进:从现在到未来 在过去的十年里,互联网技术发生了飞速变化。无论是大数据、人工智能,还是5G网络和物联网,每一种技术的突破都在改变我们的生活方式和工作模式。作为现代社会的核心驱动力,互联网技…...
vscode安装ESLint与Vetur插件后自动修复代码不生效
vscode安装ESLint与Vetur插件后自动修复代码不生效 1、安装ESLint 和 Vuter 2、运行结果 2.1、代码保存时代码中的分号;能被检测出来,但是不会自动修复 2.2、手动运行ESLint 修复命令(在终端中执行 npx eslint . --fix)可以修复问题 3、解决办法 在.vscode目录下setti…...
2848、与车相交的点
2848、[简单] 与车相交的点 1、题目描述 给你一个下标从 0 开始的二维整数数组 nums 表示汽车停放在数轴上的坐标。对于任意下标 i,nums[i] [starti, endi] ,其中 starti 是第 i 辆车的起点,endi 是第 i 辆车的终点。 返回数轴上被车 任意…...
基于k8s手动部署rabbitmq集群(Manually Deploying RabbitMQ Cluster Based on k8s)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
