【Go】探索Go语言中的Map

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Go语言开发零基础到高阶实战
景天的主页:景天科技苑

在这里插入图片描述

文章目录

  • Go语言中的Map
    • 1. Map的基本用法
      • 1.1 创建Map
      • 1.2 添加键值对
      • 1.3 获取值
      • 1.4 判断键是否存在
      • 1.5 删除键值对
      • 1.6 遍历Map
      • 1.7 获取Map的长度
    • 2. Map的进阶用法
      • 2.1 使用Map实现类似Set的功能
      • 2.2 Map作为函数参数
      • 2.3 Map的嵌套
      • 2.4 Map的并发访问
    • 3. Map的底层实现
      • 3.1 Hash表与Bucket
      • 3.2 Hash冲突与解决
      • 3.3 负载因子与扩容
      • 3.4 扩容策略
    • 4. 实际应用案例
      • 4.1 命令行工具的实现
      • 4.2 缓存系统的实现
      • 4.3 多字段查询的实现
      • 4.3 示例:多字段查询的实现
      • 4.4 注意事项
    • 5. 总结

Go语言中的Map

Go语言中的Map是一种内置的数据结构,它提供了一种通过键(Key)来访问值(Value)的高效方式。Map是无序的键值对集合,其中每个键在Map中都是唯一的,且Map的键和值可以是任意类型(但键必须是可比较的类型,如整数、浮点数、字符串等)。在Go语言中,Map的灵活性和高效性使其成为处理复杂数据结构的首选。

1. Map的基本用法

1.1 创建Map

在Go语言中,可以使用内置的make函数来创建一个空的Map。make函数的语法为make(map[keyType]valueType),其中keyTypevalueType分别代表键和值的类型。以下是一个简单的例子:

m := make(map[string]int) // 创建一个键为字符串,值为整数的Map

另外,还可以使用字面量初始化的方式来创建并初始化Map,例如:

test := map[string]int{"测试1": 1,"测试2": 2,
}

1.2 添加键值对

向Map中添加键值对非常简单,只需使用map[key] = value的语法即可。如果键已存在,则更新其对应的值。

m := make(map[string]int)
m["apple"] = 5
m["banana"] = 3

1.3 获取值

通过键来获取对应的值,可以使用map[key]的语法。如果键不存在,则返回一个该类型的零值(例如,对于int类型,零值是0)。

fmt.Println(m["apple"]) // 输出: 5

1.4 判断键是否存在

在获取值的时候,有时候需要判断键是否存在。可以使用“逗号ok”语法来实现,如果键存在,则ok为true,否则为false。

value, ok := m["orange"]
if ok {fmt.Println("orange的值为:", value)
} else {fmt.Println("orange不存在")
}

1.5 删除键值对

要删除Map中的键值对,可以使用delete函数,其语法为delete(map, key)。如果键不存在,delete函数也不会报错,相当于空操作。

delete(m, "banana")

1.6 遍历Map

使用for循环和range关键字可以遍历Map中的键值对。遍历的顺序是不确定的,因为Map是无序的。

for key, value := range m {fmt.Println(key, ":", value)
}

1.7 获取Map的长度

可以使用len函数获取Map中键值对的个数。

fmt.Println("map的长度为:", len(m))

2. Map的进阶用法

2.1 使用Map实现类似Set的功能

虽然Go语言没有直接提供Set类型,但可以使用Map来实现类似Set的功能。由于Map的键是唯一的,可以将键用作Set中的元素。

var mySet map[string]bool
mySet = make(map[string]bool)// 添加元素
mySet["apple"] = true
mySet["banana"] = true// 遍历Set
for key := range mySet {fmt.Println(key)
}// 检查元素是否存在
if _, exists := mySet["apple"]; exists {fmt.Println("apple exists")
}

2.2 Map作为函数参数

Map可以作为函数的参数传递。需要注意的是,传递的是Map的引用,而不是Map的副本。因此,在函数中对Map的修改会影响到原始Map。

func ModifyMap(m map[string]int, key string, value int) {m[key] = value
}func main() {m := make(map[string]int)m["apple"] = 5ModifyMap(m, "apple", 10)fmt.Println(m["apple"]) // 输出: 10
}

2.3 Map的嵌套

Map可以嵌套使用,即Map的值可以是另一个Map。这种结构在处理复杂数据结构时非常有用。

// 嵌套Map示例
nestedMap := make(map[string]map[string]int)
nestedMap["fruits"] = make(map[string]int)
nestedMap["fruits"]["apple"] = 5
nestedMap["fruits"]["banana"] = 3fmt.Println(nestedMap["fruits"]["apple"]) // 输出: 5

2.4 Map的并发访问

在并发环境下,普通的Map不是并发安全的。如果多个goroutine同时读写同一个Map,可能会导致竞态条件。为了解决这个问题,Go语言标准库提供了sync.Map类型,它是专门为并发环境设计的。

var g_syncMap sync.Mapfunc main() {// 添加元素g_syncMap.Store(1, "one")g_syncMap.Store(2, "two")// 遍历g_syncMap.Range(func(key, value interface{}) bool {fmt.Printf("key: %v, value: %v\n", key, value)return true})// 删除元素g_syncMap.Delete(1)// Load或LoadOrStoreif v, ok := g_syncMap.Load(2); ok {fmt.Println("Loaded:", v)}if loaded, ok := g_syncMap.LoadOrStore(3, "three"); !ok {fmt.Println("Stored:", loaded)}
}

3. Map的底层实现

3.1 Hash表与Bucket

Go语言的Map使用Hash表作为底层实现。Hash表是一种通过哈希函数组织数据,以支持快速插入和搜索的数据结构。在Go的Map中,一个哈希表可以有多个Bucket,每个Bucket可以保存一个或一组键值对。

3.2 Hash冲突与解决

Hash冲突是指不同的键经过哈希函数计算后得到相同的哈希值。Go采用链地址法(也称为开放寻址法的一种变种)来解决Hash冲突。当一个Bucket存放的键值对超过一定数量(Go中为8个)时,会创建一个新的Bucket,并将新的键值对添加到新的Bucket中,同时用指针将两个Bucket链接起来。

3.3 负载因子与扩容

负载因子是衡量Hash表冲突情况的一个指标,其计算公式为:负载因子 = 键数量 / Bucket数量。Go的Map在负载因子达到6.5时会触发扩容,以减少冲突并提高访问效率。扩容时,会创建一个新的Bucket数组,其长度是原来的两倍,然后将旧Bucket数组中的元素搬迁到新的Bucket数组中。

3.4 扩容策略

Go的Map采用逐步搬迁的策略来减少扩容时的延时。每次访问Map时都会触发一次搬迁,但每次只搬迁两个键值对。这种策略使得扩容过程更加平滑,减少了因一次性搬迁大量数据而导致的性能问题。

4. 实际应用案例

4.1 命令行工具的实现

在开发命令行工具时,经常需要根据不同的命令名称调用不同的函数。使用Map可以很方便地实现这一功能。定义一个Map,其键为命令名称,值为函数指针。这样,在接收到命令时,只需根据命令名称从Map中查找对应的函数指针并调用即可。

var FuncMap = map[string]func(int, string){"111": map1,"222": map2,
}func map1(a int, b string) {fmt.Println("111", a, b)
}func map2(a int, b string) {fmt.Println("222", a, b)
}func test1(id string) {a := 250b := "25.250"if fn, ok := FuncMap[id]; ok {fn(a, b)} else {fmt.Println(id, "not found func")}
}

4.2 缓存系统的实现

在开发需要缓存数据的系统时,Map是一个很好的选择。可以使用Map来存储缓存的数据,其中键为缓存的标识(如请求的URL),值为缓存的数据。当需要访问数据时,首先检查Map中是否存在对应的键,如果存在则直接返回缓存的数据,否则进行数据的加载和缓存。

4.3 多字段查询的实现

在处理具有多个字段的数据时,可能需要根据不同的字段进行查询。此时,可以使用多个Map来实现多字段查询。主Map的键为唯一标识符(如ID),值为数据对象。另外,可以创建多个辅助Map,每个辅助Map的键为不同的查询字段(如姓名、邮箱等),值为主Map中的键(即唯一标识符)。这样,就可以通过不同的查询字段快速定位到数据对象。

4.3 示例:多字段查询的实现

假设我们有一个用户系统,需要支持通过用户ID、姓名或邮箱来查询用户信息。我们可以使用Go语言的Map来实现这样的功能。

首先,定义用户信息的结构体:

type UserInfo struct {ID       stringName     stringEmail    string// 其他字段...
}

然后,创建主Map和辅助Map来存储用户信息:

// 主Map,以用户ID为键,UserInfo为值
usersByID := make(map[string]UserInfo)// 辅助Map,以姓名为键,用户ID为值
usersByName := make(map[string]string)// 辅助Map,以邮箱为键,用户ID为值
usersByEmail := make(map[string]string)// 示例数据
usersByID["1"] = UserInfo{ID: "1", Name: "Alice", Email: "alice@example.com"}
usersByID["2"] = UserInfo{ID: "2", Name: "Bob", Email: "bob@example.com"}// 填充辅助Map
for _, user := range usersByID {usersByName[user.Name] = user.IDusersByEmail[user.Email] = user.ID
}

现在,我们可以根据用户ID、姓名或邮箱来查询用户信息了:

// 通过用户ID查询
if user, ok := usersByID["1"]; ok {fmt.Printf("User ID: %s, Name: %s, Email: %s\n", user.ID, user.Name, user.Email)
}// 通过姓名查询
if userID, ok := usersByName["Alice"]; ok {if user, ok := usersByID[userID]; ok {fmt.Printf("User ID: %s, Name: %s, Email: %s\n", user.ID, user.Name, user.Email)}
}// 通过邮箱查询
if userID, ok := usersByEmail["alice@example.com"]; ok {if user, ok := usersByID[userID]; ok {fmt.Printf("User ID: %s, Name: %s, Email: %s\n", user.ID, user.Name, user.Email)}
}

4.4 注意事项

  • 在使用Map作为缓存时,需要注意内存的使用情况,避免缓存过多数据导致内存溢出。
  • 当Map中的数据需要频繁更新时,需要考虑并发安全的问题。对于非并发的场景,可以使用普通的Map;对于并发的场景,可以考虑使用sync.Map或通过互斥锁(如sync.Mutex)来保护Map的访问。
  • 在使用多字段查询时,辅助Map中的值(如用户ID)应该是唯一的,以避免出现冲突。如果可能出现冲突(例如,两个用户可能有相同的姓名),则需要在查询时额外处理这种情况。

5. 总结

Go语言中的Map是一种非常强大且灵活的数据结构,它提供了通过键来快速访问值的能力。通过合理使用Map,可以高效地处理各种复杂的数据结构和查询需求。在本文中,我们详细介绍了Map的基本用法、进阶用法、底层实现以及实际应用案例,希望能够帮助读者更好地理解和使用Go语言中的Map。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/148652.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot整合InfluxDB(实战)

一、简单介绍InfluxDB是什么? InfluxDB是一个由InfluxData开发的开源时序型数据。它由Go写成,着力于高性能地查询与存储时序型数据。InfluxDB被广泛应用于存储系统的监控数据,IoT行业的实时数据等场景。 1、主要特点 时间序列数据存储 专门…

【机器学习】生成对抗网络(GAN)——生成新数据的神经网络

生成对抗网络(Generative Adversarial Networks,简称GAN)是一种创新的神经网络结构,近年来在机器学习和人工智能领域引起了广泛的关注。GAN的核心思想是通过两个神经网络的对抗性训练,生成高质量的、与真实数据相似的新…

FastAPI 第二课 -- 安装

目录 一. 前言 二. 运行第一个 FastAPI 应用 一. 前言 FastAPI 依赖 Python 3.8 及更高版本。 安装 FastAPI 很简单,这里我们使用 pip 命令来安装。 pip install fastapi 另外我们还需要一个 ASGI 服务器,生产环境可以使用 Uvicorn 或者 Hypercorn…

构建 Spring Data JPA 项目所需的依赖与配置

一、使用 Spring Boot Initializr 添加依赖的步骤(IntelliJ IDEA 中的操作) 打开 IntelliJ IDEA,选择 New Project > Spring Initializr。填写项目的 Group、Artifact、Project Metadata 等基础信息。选择 Maven Project,并选…

函数模板进阶 - 为什么函数模板不要特化?

本文参考文章2001 年 7 月的 C/C++ Users Journal,第 19 卷第 7 期:Why Not Specialize Function Templates? 大家有兴趣可以看看原文。 文章目录 一、 重载和特化1. 重载2. 特化二、特化和重载的调用优先级1. 第一份代码2. 第二份代码3. 原因三、函数模板特化的书写格式1. …

扩散模型和表示学习(Diffusion Models and Representation Learning)

Diffusion Models专栏文章汇总:入门与实战 前言:扩散模型是各种视觉任务中流行的生成建模方法,引起了人们的广泛关注。它们可以被认为是自监督学习方法的一个独特实例,因为它们独立于标签注释。这篇博客讨论扩散模型与表征学习之间…

《linux系统》基础操作

二、综合应用题(共50分) 随着云计算技术、容器化技术和移动技术的不断发展,Unux服务器已经成为全球市场的主导者,因此具备常用服务器的配置与管理能力很有必要。公司因工作需要,需要建立相应部门的目录,搭建samba服务器和FTP服务器,要求将销售部的资料存放在samba服务器…

Android15之编译Cuttlefish模拟器(二百三十一)

简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【…

托盘检测系统源码分享

托盘检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

电脑误删文件回收站清空了怎么找回文件?

在日常工作和生活中,电脑已成为我们不可或缺的工具。然而,随着使用频率的增加,误删文件的情况也时有发生。更为糟糕的是,有时候我们在清空回收站后才意识到误删了重要文件。面对这种情况,很多人可能会感到绝望&#xf…

MatrixOne 助力某电信运营商构建低成本高性能车联网管理系统

客户基本情况 该电信运营商在物联网领域深耕多年,致力于为企业和个人提供全面的物联网解决方案,包括智能连接、设备管理、数据采集与分析等核心服务。凭借其强大的网络覆盖和技术优势,该运营商为各行业提供高效、安全、可靠的物联网服务&…

【算法业务】基于Multi-Armed Bandits的个性化push文案自动优选算法实践

1. 背景介绍 该工作属于多年之前的用户增长算法业务项目。在个性化push中,文案扮演非常重要的角色,是用户与push的商品之间的桥梁,文案是用户最直接能感知的信息。应该说在push产品信息之外,最重要的就是文案,直接能…

【二等奖论文】2024年华为杯研究生数学建模F题成品论文

您的点赞收藏是我继续更新的最大动力! 一定要点击如下的卡片,那是获取资料的入口! 【全网最全】2024年华为杯研赛F题保奖思路matlab/py代码成品论文等(后续会更新完整 点击链接加入群聊【2024华为杯研赛资料汇总】:ht…

BUUCTF-MISC-荷兰宽带数据泄露

下载附件得到一个二进制文件 通过题目猜测这是一段路由器备份日志,可以使用RouterPassView打开 链接: https://pan.baidu.com/s/1tY5Sdl8GcI5dKQdhPXj5yA?pwdhi9k 下载链接http://pan.baidu.com/s/1tY5Sdl8GcI5dKQdhPXj5yA?pwdhi9k注意,这个软件会报毒…

二、电脑入门2之常用dos命令

打开dos命令窗口 win R 常用dos命令 dir: 列出当前目录下的所有文件以及目录 cls :清理屏幕 exit: 关闭dos命令窗口 c:(盘字母后带冒号) 切换盘符 del: 删除文件 ipconfig : 查看IP信息 ipconfig/all &#xf…

导入时,文档模板不被下载

问题描述 提示:这里描述项目中遇到的问题: 这是个SSM项目,以前经常遇到这个问题,今天有幸记录下来 [ERROR][o.a.s.r.StreamResult] Can not find a java.io.InputStream with the name [downLoadFile] in the invocation stack…

Apache CVE-2021-41773 漏洞复现

1.打开环境 docker pull blueteamsteve/cve-2021-41773:no-cgid docker run -d -p 8080:80 97308de4753d 2.访问靶场 3.使用poc curl http://47.121.191.208:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd 4.工具验证

uni-icons自定义图标详细步骤及踩坑经历

一、详细步骤 获取图标 1.访问iconfont-阿里巴巴矢量图标库,搜索图标并加入购物车: 2.点击页面右上角购物车图标 ,点击添加至项目,如没有项目,需要点击下图第二步的图标新建一个项目目录,如已经有项目则…

Leetcode面试经典150题-39.组合总数进阶:40.组合总和II

本题是扩展题,真实考过,看这个题之前先看一下39题 Leetcode面试经典150题-39.组合总数-CSDN博客 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数…

sql-labs靶场

第一关(get传参,单引号闭合,有回显,无过滤) ?id-1 union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schemasecurity) -- 第二关(get传参,无闭…