【GoLang基础】map是什么?

问题引出:

Go语言中map是什么?

解答:

map 是一种集合型数据结构,用于存储键值对,并提供了快速的查找、插入和删除操作。以下是更深入的介绍和使用 map 的注意事项:

1. 声明和初始化

在 Go 中声明和初始化 map 有几种常见的方式:

  • 使用 make() 函数来创建一个空的 map
m := make(map[keyType]valueType)

示例:

ages := make(map[string]int)
  • 使用字面量进行初始化:
m := map[keyType]valueType{key1: value1,key2: value2,// 可以初始化多个键值对
}

示例:

ages := map[string]int{"Alice": 30,"Bob":   25,"Eve":   28,
}

2. 插入和访问元素

map 中插入或更新元素使用语法 map[key] = value,通过键来访问元素使用语法 map[key]

示例:

ages["Charlie"] = 35       // 插入键值对
fmt.Println(ages["Bob"])   // 访问键为 "Bob" 的值

时间复杂度: 向 map 中 插入新的键值对或更新已有键的值,时间复杂度是 O(1)。在哈希表中,通过计算键的哈希值,确定存储位置,然后在该位置执行插入或更新操作;根据给定的键查找对应的值,时间复杂度是 O(1)。同样,通过计算键的哈希值,确定存储位置,然后直接访问该位置的值。

3. 删除元素

可以使用 delete() 函数从 map 中删除指定键的元素:

delete(ages, "Eve")        // 删除键为 "Eve" 的键值对

时间复杂度: 删除 map 中指定键的元素,时间复杂度也是 O(1)。首先根据键计算哈希值,定位到存储位置,然后执行删除操作。

4. 检查键是否存在

在访问 map 中的元素时,可以通过多返回值的方式检查指定的键是否存在:

value, ok := ages["Alice"]
if ok {fmt.Println("Alice's age is", value)
} else {fmt.Println("Alice not found in the map")
}

5. 遍历 map

使用 for range 结构可以遍历 map 中的所有键值对:

for key, value := range ages {fmt.Println(key, "is", value, "years old")
}

6. map 的注意事项

  • map 是引用类型,不需要使用 & 来获取其地址。
  • map 的零值是 nil,未初始化的 map 是不能存放键值对的,对 nilmap 执行读写操作会导致运行时 panic
  • map 的键类型必须支持相等运算符(==、!=),如果键类型是切片、函数或包含切片的结构类型,则不能用作 map 的键。
  • map 是无序的,每次迭代 map 时,输出的顺序可能会不同。
  • 在并发环境下,对 map 的读写需要加锁保护,或者使用 sync.Map(Go 1.9 引入的并发安全的 map 类型)。

示例
下面是一个完整的示例,演示了 map 的声明、初始化、插入、访问、删除和遍历操作:

package mainimport "fmt"func main() {// 声明并初始化一个 mapcolors := map[string]string{"red":    "#ff0000","green":  "#00ff00","blue":   "#0000ff",}// 向 map 中插入新的键值对colors["white"] = "#ffffff"colors["black"] = "#000000"// 访问 map 中的元素fmt.Println("Hex code for red:", colors["red"])// 检查键是否存在if hexCode, exists := colors["blue"]; exists {fmt.Println("Hex code for blue:", hexCode)} else {fmt.Println("Blue color not found")}// 删除 map 中的元素delete(colors, "green")// 遍历 map 中的所有键值对fmt.Println("All colors:")for color, hexCode := range colors {fmt.Printf("%s -> %s\n", color, hexCode)}
}

7. map的使用场景

  1. 数据索引和快速查找:使用 map 可以构建数据索引,以便快速查找和访问数据。例如,将用户 ID 映射到用户信息的 map,可以通过 ID 快速查找用户信息。
userMap := map[int]User{1: {ID: 1, Name: "Alice", Age: 30},2: {ID: 2, Name: "Bob", Age: 25},// 更多用户信息...
}// 查找用户 ID 为 2 的信息
if user, ok := userMap[2]; ok {fmt.Println("User found:", user.Name)
} else {fmt.Println("User not found")
}
  1. 统计和计数:使用 map 可以方便地统计和计数元素出现的次数。例如,统计一段文字中每个单词出现的次数。
text := "hello world hello go world"
wordCount := make(map[string]int)// 统计每个单词出现的次数
words := strings.Fields(text)
for _, word := range words {wordCount[word]++
}// 输出单词出现的次数
for word, count := range wordCount {fmt.Printf("%s: %d\n", word, count)
}
  1. 缓存数据:使用 map 可以实现简单的缓存机制,将计算结果缓存起来以提高性能。例如,缓存函数的计算结果。
type Memo struct {cache map[string]int
}func (m *Memo) Fibonacci(n int) int {if val, ok := m.cache[strconv.Itoa(n)]; ok {return val}if n <= 1 {return n}result := m.Fibonacci(n-1) + m.Fibonacci(n-2)m.cache[strconv.Itoa(n)] = resultreturn result
}func main() {memo := Memo{cache: make(map[string]int)}fmt.Println(memo.Fibonacci(10))  // 计算并缓存 Fibonacci(10)fmt.Println(memo.Fibonacci(20))  // 从缓存中获取 Fibonacci(20)
}
  1. 配置管理:使用 map 可以存储和管理配置信息。例如,将配置项的名称作为键,配置值作为值存储在 map 中,方便动态读取和更新配置。
config := map[string]string{"host":     "localhost","port":     "8080","database": "mydb",// 更多配置项...
}// 获取配置项
fmt.Println("Database host:", config["host"])// 更新配置项
config["port"] = "9090"
  1. 状态管理:使用 map 可以方便地存储和管理程序的状态信息。例如,存储用户的登录状态或权限信息。
type UserStatus map[string]boolfunc main() {userStatus := make(UserStatus)userStatus["alice"] = trueuserStatus["bob"] = false// 检查用户状态if loggedIn, ok := userStatus["alice"]; ok && loggedIn {fmt.Println("Alice is logged in")} else {fmt.Println("Alice is not logged in")}
}

8. map 的底层数据结构,如何实现快速查找?

map 在 Go 语言中的底层数据结构是哈希表(hash table),也称为哈希映射。哈希表是一种常用的数据结构,用于实现键值对的存储和快速查找。下面解释 map 如何使用哈希表来实现快速查找:

map 的实现方式:

  1. 哈希函数
    当向 map 中插入键值对或查找键时,Go 语言会使用内置的哈希函数计算键的哈希值。
    哈希函数将键的内容映射成一个固定大小的整数,这个整数就是键的哈希值。
  2. 存储桶(Buckets)
    map 内部维护一个存储桶数组(bucket array),每个存储桶中存储着键值对。
    哈希值确定了键值对存储在哪个存储桶中。具体的存储位置通过哈希值的某种映射方式(通常是取模运算)计算得出。
  3. 解决哈希冲突
    哈希函数并不是完美的,不同的键可能产生相同的哈希值(称为哈希冲突)。
    map 内部会使用一种解决冲突的方法,常见的方式是使用链表或者更高效的红黑树(在元素较多时)来存储同一个存储桶中的键值对。

实现快速查找的过程:

  • 插入键值对:
    1.计算键的哈希值。
    2.根据哈希值确定存储桶的位置。
    3.将键值对存储在对应存储桶中。
  • 查找键:
    1.计算要查找的键的哈希值。
    2.根据哈希值确定存储桶的位置。
    3.在该位置的链表或红黑树中查找指定的键。
  • 删除键:
    1.计算要删除的键的哈希值。
    2.根据哈希值确定存储桶的位置。
    3.在该位置的链表或红黑树中删除指定的键值对。

9. map 在并发环境中是否安全?

map 在并发环境中不是安全的,即不支持并发读写操作。这是因为 map 的操作涉及到读取和修改底层的数据结构,而哈希表(map 的底层实现)在并发读写时可能会导致数据竞态(data race)和意外行为。

为了在并发环境中安全地使用 map,有以下两种常用的方法:

  • 加锁保护
    可以使用互斥锁(sync.Mutex)或读写锁(sync.RWMutex)来保护 map,在读取或修改 map 时进行加锁操作,防止多个 goroutine 并发访问。
var mu sync.Mutex
m := make(map[string]int)// 写入操作需要加锁保护
mu.Lock()
m["key"] = 123
mu.Unlock()// 读取操作也需要加锁保护
mu.Lock()
value := m["key"]
mu.Unlock()
  • 使用 sync.Map(Go 1.9 引入的并发安全的 map 类型)
    sync.Map 提供了一种并发安全的键值对存储和访问方式,无需手动加锁。
var m sync.Map// 存储键值对
m.Store("key", 123)// 加载键对应的值
value, ok := m.Load("key")
if ok {fmt.Println(value)
}// 删除指定键
m.Delete("key")

小结:

map 是 Go 语言中非常重要且常用的数据结构,用于存储和操作键值对数据。合理地使用 map 可以简化代码,并提高程序的性能和可读性。在使用 map 时,需要注意其特性和使用方法,避免出现意外的错误和行为。

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

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

相关文章

【java.io.IOException: java.lang.IllegalArgumentException: db.num is null】

默认用户名&#xff1a;nacos 密码&#xff1a;nacos解决方法&#xff1a; a)在conf目录下将nacos-mysql.sql脚本创建完成&#xff1b; b)修改application.properties&#xff0c;在内容里添加如下内容 spring.datasource.platformmysql db.num1 db.url.0jdbc:mysql://localho…

网络安全零信任学习2:零信任概念

《白话零信任》第2章&#xff1a; 零信任假设最坏的情况已经发生&#xff0c;一切都不可信&#xff0c;在此基础上执行 最严格的动态持续认证和访问控制策略。 &#xff08;1&#xff09;网络不可信&#xff1a;网络始终充满威胁&#xff0c;内网与外网没有不同&#xff0c;网络…

有刷电机、无刷电机

阅读引言&#xff1a; 最近在备赛&#xff0c; 自己之前虽然用过电机&#xff0c; 但是发现在一些高要求的应用场景&#xff0c; 发现自己对电机的知识理解得不是很透彻&#xff0c; 所以写下这篇文章。 目录 一、 有刷电机内部原理 二、有刷电机一些关键参数 三、无刷电机内…

2024OD机试卷-英文输入法 (java\python\c++)

题目:英文输入法 题目描述 主管期望你来实现英文 输入法 单词联想功能。 需求如下: 依据用户输入的单词前缀,从已输入的英文语句中联想出用户想输入的单词,按字典序输出联想到的单词序列, 如果联想不到,请输出用户输入的单词前缀。 注意: 英文单词联想时,区分大小写缩…

机器学习初学者 6 个核心算法!建议收藏,反复观看!

今天再来介绍机器学习算法的基本概念和适用场景&#xff01; 首先&#xff0c;引用一句英国统计学家George E. P. Box的名言&#xff1a;All models are wrong, but some are useful. 没有哪一种算法能够适用所有情况&#xff0c;只有针对某一种问题更有用的算法。 也就是说&…

STM32理论 —— μCOS-Ⅲ(新)

文章目录 1. 任务调度器1.1 抢占式调度 μCos-Ⅲ全称是Micro C OS Ⅲ&#xff0c;由Micriμm 公司发布的一个基于C 语言编写的第三代小型实时操作系统(RTOS)&#xff1b; RTOS 与裸机相比最大的优势在于多任务管理与实时性&#xff0c;它提供了多任务管理和任务间通信的功能&a…

scala案例-- 九九乘法表

要求&#xff1a;打出九九乘法表 object test {def main(args: Array[String]): Unit {for (i <- 1 to 9) { //外循环控制行数for (j <- 1 to i) { //内循环控制列数print(s"${j} * ${i} ${i j}\t")}println()}} }结果&#xff1a; 1 * 1 2 1 * 2 …

一文玩转Vue3参数传递——全栈开发之路--前端篇(8)

全栈开发一条龙——前端篇 第一篇&#xff1a;框架确定、ide设置与项目创建 第二篇&#xff1a;介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇&#xff1a;setup语法&#xff0c;设置响应式数据。 第四篇&#xff1a;数据绑定、计算属性和watch监视 第五篇 : 组件…

电脑桌面定时提醒软件 有定时提醒功能的软件有哪些

对于很多上班族来说&#xff0c;在电脑、手机上使用一款定时提醒软件&#xff0c;是非常有必要的。一款定时提醒软件能让我们对即将进行的工作一目了然&#xff0c;防止遗漏或错过重要事务。其次&#xff0c;通过设定提醒&#xff0c;我们可以更好地安排自己的时间&#xff0c;…

基于springboot实现医院药品管理系统项目【项目源码+论文说明】

基于springboot实现医院药品管理系统演示 摘要 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得到提升&#xff0c;而读书就…

图书管理系统c语言

创建一个图书管理系统是一个涉及数据结构和文件操作的项目。在C语言中&#xff0c;你可以使用结构体来表示图书信息&#xff0c;使用函数来实现系统的各项功能。以下是一个简单的图书管理系统的示例&#xff0c;包括基本的添加、显示、查找和删除图书的功能。 1. 定义图书结构…

社交媒体数据恢复:新浪微博

当我们在使用新浪微博时&#xff0c;可能会遇到一些意外情况&#xff0c;如误删微博、账号出现问题等。这时&#xff0c;我们需要进行数据恢复。本文将详细介绍如何在新浪微博中进行数据恢复。 首先&#xff0c;我们需要了解新浪微博的数据恢复功能。根据微博的帮助中心&#…

Android11 InputManagerService启动流程分析

InputManagerService在systemserver进程中被启动 //frameworks\base\services\java\com\android\server\SystemServer.java t.traceBegin("StartInputManagerService"); inputManager new InputManagerService(context);//1 t.traceEnd(); //省略 //注册服务 Servi…

Spring Data JPA进行数据库操作

使用Spring Data JPA进行数据库操作涉及几个关键步骤&#xff0c;包括配置、定义实体类、创建仓库接口以及执行具体的数据库操作。以下是详细的过程&#xff1a; 1. 添加依赖 首先&#xff0c;在项目的pom.xml文件中添加Spring Data JPA的依赖&#xff1a; <dependencies…

socket实现TCP UDP

1、socket通信建立流程 1.1、创建服务端流程 使用 socket 函数来创建 socket服务。 使用 bind 函数绑定端口。 使用 listen 函数监听端口。 使用 accept 函数接收客户端请求。 1.2、创建客户端流程 使用 socket 函数来创建 socket 服务。 使用 connect 函数连接到 socke…

基于springboot+jsp+Mysql的商务安全邮箱邮件收发

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

【C++ STL 容器】map 和 set 详解

文章目录 1.什么是关联式容器呢&#xff1f;2.键值对的定义 1.什么是关联式容器呢&#xff1f; &#x1f34e;① vector、list、deque、forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身…

【强训笔记】day16

NO.1 代码实现&#xff1a; class StringFormat { public:string formatString(string A, int n, vector<char> arg, int m) {string ret;int j0;for(int i0;i<n;i){if(A[i]%){if(i1<n&&A[i1]s){retarg[j];i;}else {retA[i];}}else {retA[i];}}while(j&l…

设计模式之拦截过滤器模式

想象一下&#xff0c;在你的Java应用里&#xff0c;每个请求就像一场冒险旅程&#xff0c;途中需要经过层层安检和特殊处理。这时候&#xff0c;拦截过滤器模式就化身为你最可靠的特工团队&#xff0c;悄无声息地为每一个请求保驾护航&#xff0c;确保它们安全、高效地到达目的…

小白必看:数据防泄密软件介绍|安在云和Ping32对比?

在当今数字化时代&#xff0c;数据防泄密软件已经成为企业和组织不可或缺的重要工具。随着信息技术的发展&#xff0c;企业面临着越来越多的网络安全威胁&#xff0c;数据泄露事件也屡见不鲜。数据防泄密软件的出现&#xff0c;为企业提供了有效的解决方案。 一、数据防泄密软…