【Go】探索Go语言中的Map

news2024/9/24 8:50:13

在这里插入图片描述

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

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

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,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"] = 5

    ModifyMap(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"] = 3

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

2.4 Map的并发访问

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

var g_syncMap sync.Map

func 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或LoadOrStore
    if 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 := 250
    b := "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       string
    Name     string
    Email    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.ID
    usersByEmail[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.coloradmin.cn/o/2159936.html

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

相关文章

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23 本期,我们对大语言模型在表情推荐, 软件安全和 自动化软件漏洞检测等方面如何应用,提供几篇最新的参考文章。 1 Semantics Preserving Emoji Recommendation with Large Language Mod…

高清8k电脑壁纸分享

Hello!欢迎各位新老朋友来看小弟博客,祝大家事业顺利,财源广进!! 主题:高清壁纸分享 文件太大上传不上去😅😅😅😅😅,需要的朋友自取&…

C语言 | Leetcode C语言题解之第433题最小基因变化

题目&#xff1a; 题解&#xff1a; int minMutation(char * start, char * end, char ** bank, int bankSize) {int m strlen(start);int **adj (int **)malloc(sizeof(int *) * bankSize);int endIndex -1;for (int i 0; i < bankSize; i) {adj[i] (int *)malloc(si…

手势手语识别数据集,YOLOv5目标检测两个数据集,图大量,模型精度高分别为近100类

手势手语识别数据集&#xff0c;YOLOv5目标检测 两个数据集&#xff0c;图大量&#xff0c;模型精度高分别为近100类 手势手语识别数据集&#xff08;Sign Language Gesture Recognition Dataset&#xff09; 数据集概述 该数据集专为手势手语识别设计&#xff0c;包含大量的…

Android平台Unity3D下如何同时播放多路RTMP|RTSP流?

技术背景 好多开发者&#xff0c;提到希望在Unity的Android头显终端&#xff0c;播放2路以上RTMP或RTSP流&#xff0c;在设备性能一般的情况下&#xff0c;对Unity下的RTMP|RTSP播放器提出了更高的要求。实际上&#xff0c;我们在前几年发布Unity下直播播放模块的时候&#xf…

某采招网爬虫数据采集逆向

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 目标网站 aHR0cHM6Ly9zZWFyY2guYmlkY2VudGVyLmNvbS5jbi9zZWFyY2g/a2V5d29yZHM9JWU0…

并发编程工具集——CompletionService(三十五)

利用 CompletionService 实现 Dubbo 中的 Forking Cluster Dubbo 中有一种叫做 Forking 的集群模式&#xff0c;这种集群模式下&#xff0c;支持并行地调用多个查询服务&#xff0c;只要有一个成功返回结果&#xff0c;整个服务就可以返回了。例如你需要提供一个地址转坐标的服…

MySQL之基本查询(二)(update || delete || 聚合函数 || group by)

目录 一、表的更新update 二、表的删除delete 三、聚合函数 四、group by 分组查询 一、表的更新update 语法&#xff1a; UPDATE table_name SET column expr [, column expr ...] [WHERE ...] [ORDER BY ...] [LIMIT ...] 使用实列&#xff1a; ~ 将孙悟空同学的数学…

小程序开发设计-小程序的宿主环境:宿主环境简介⑥

上一篇文章导航&#xff1a; 小程序开发设计-小程序代码的构成&#xff1a;小程序页面的组成部分详解⑤-CSDN博客https://blog.csdn.net/qq_60872637/article/details/142306902?spm1001.2014.3001.5501 注&#xff1a;不同版本选项有所不同&#xff0c;并无大碍。 目录 上…

centos7安装docker DokcerCompose

一, 安装docker 1.更新yum源 yum下载很慢&#xff0c;一直出现正在尝试其它镜像&#xff0c;更改yum地址为阿里云镜像即可 1&#xff09;下载了阿里云提供的CentOS 7的Yum源配置文件&#xff0c;并将其覆盖到系统中的 /etc/yum.repos.d/CentOS-Base.repo 文件。 wget -O /et…

CVE-2024-44902 Thinkphp反序列化漏洞

Thinkphp v6.1.3至v8.0.4版本中存在反序列化漏洞&#xff0c;攻击者可利用此漏洞执行任意代码。 影响版本 v6.1.3 < thinkphp < v8.0.4 环境搭建 环境&#xff1a;php8.0.2thinkphp8.0.4memcached3.2.0 首先搭建 thinkphp 环境&#xff1a;thinkPHP 8.0.4 安装_thin…

SQL进阶技巧:如何利用if语句简化where或join中的条件 | if条件语句的优雅使用方法

目录 0 问题场景 1 数据准备 2 问题分析 2.1 需求一 2.2需求二 3 小结 想要进一步了解SQL这门艺术语言的&#xff0c;可以订阅我的专栏数字化建设通关指南&#xff0c;将在该专栏进行详细解析。 专栏 原价99&#xff0c;现在活动价39.9&#xff0c;按照阶梯式增长&…

EasyExcel将数据库里面的数据生成excel文件

EasyExcel官方文档 1.在model模块导入依赖 <!-- 生成报表--> <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>4.0.3</version> </dependency> 2.修饰实体类 package…

Metasploit Pro 4.22.4-2024091601 发布下载,新增功能概览

Metasploit Pro 4.22.4-2024091601 (Linux, Windows) - 专业渗透测试框架 Rapid7 Penetration testing, release Sep 16, 2024 请访问原文链接&#xff1a;https://sysin.org/blog/metasploit-pro-4/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页…

保障电气安全的电气火灾监控系统主要组成有哪些?

电气火灾是什么&#xff1f; 电气火灾一般是指由于电气线路、用电设备、器具以及供配电设备出现故障性释放的热能&#xff1a;如高温、电弧、电火花以及非故障性释放的能量&#xff1b;如电热器具的炽热表面&#xff0c;在具备燃烧条件下引燃本体或其他可燃物而造成的火灾&…

速通汇编(七)BX、SI、DI寄存器,BP寄存器,直接寻址和间接寻址

下文中出现的"idata"&#xff0c;指的都是任意常量 一&#xff0c;基于BX、SI、DI等寄存器的寻址形式 在第五篇中曾介绍过DS寄存器的作用&#xff0c;简要复习一下->速通汇编&#xff08;五&#xff09;认识段地址与偏移地址&#xff0c;CS、IP寄存器和jmp指令&a…

oracle avg、count、max、min、sum、having、any、all、nvl的用法

组函数 having的使用 any的使用 all的使用 nvl 从执行结果来看&#xff0c;nvl(列名&#xff0c;默认值)&#xff0c;nvl的作用就是如果列名所在的这一行出现空则用默认值替换

3.2 USART 通用同步/异步收发器

文章目录 什么是USARTUSART框图 stm32的Usart串口收发配置初始化发送接收 重定向的几种方法串口发送数据包 什么是USART USART&#xff08;Universal Synchronous/Asynchronous Receiver/Transmitter&#xff09;通用同步/异步收发器USART是STM32内部集成的硬件外设&#xff0…

[Unity Demo]从零开始制作空洞骑士Hollow Knight第十集:制作后坐力系统Recoil和小骑士的生命系统和受伤系统

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作后坐力系统Recoil 1.使用代码实现扩展新的后坐力行为2.为敌人脚本添加后坐力行为3.为小骑士添加后坐力行为二、制作小骑士的生命系统和受伤系统 1.制作…

Appium独立测试自动化初始化脚本

1、查看环境初始化参数 确保appium已经开起来了&#xff0c;设置ip ,并点击启动 打开夜神模拟器&#xff0c;点击工具--设置 最下面的版本说明&#xff0c;双击进去 版本号这里再去单击。 直到进入到开发者模式。 可能我们不是开发者模式打开的状态&#xff0c;所以软件访问模…