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运维老纪的首页…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
