【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 main

import "fmt"

func main() {
    // 声明并初始化一个 map
    colors := 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)] = result
    return 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]bool

func main() {
    userStatus := make(UserStatus)
    userStatus["alice"] = true
    userStatus["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.mfbz.cn/a/608079.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年软件测试最全jmeter做接口压力测试_jmeter接口性能测试_jmeter压测接口(3),【大牛疯狂教学

既有适合小白学习的零基础资料&#xff0c;也有适合3年以上经验的小伙伴深入学习提升的进阶课程&#xff0c;涵盖了95%以上软件测试知识点&#xff0c;真正体系化&#xff01; 由于文件比较多&#xff0c;这里只是将部分目录截图出来&#xff0c;全套包含大厂面经、学习笔记、…

日志打印传值 传引用 右值引用性能测试(Linux/QNX)

结论 Linux平台和qnx平台优化后传值性能都是比传引用的差&#xff0c;也比传右值的差&#xff0c;因此传参时有必要传递引用。 测试代码 #include <cstdint> #include <ctime> #include <string>#ifdef __linux__#define ITERATIONS 10000000 #else#defin…

Windows命令行一键安装、配置WSL的方法

本文介绍在Windows电脑中&#xff0c;通过命令行的方式&#xff0c;快速、方便安装适用于Linux的Windows子系统&#xff08;Windows Subsystem for Linux&#xff0c;WSL&#xff09;的方法。 WSL是由微软开发的一项功能&#xff0c;允许在Windows操作系统上运行Linux发行版系统…

expected an expression报错

“expected an expression” 是一种编程错误&#xff0c;通常发生在程序中某个地方需要一个表达式&#xff08;expression&#xff09;的位置&#xff0c;但实际上没有提供一个有效的表达式。 据此&#xff0c;我在main.h—define宏定义中发现了问题&#xff0c;即&#xff1a;…

excel中怎么跳转到指定的单元格?

也许你会有这样的需求&#xff0c;如A1单元格中显示B100这种单元格地址&#xff0c;怎么做以点一下就跳转到B100&#xff1f; 一、设置公式 B1HYPERLINK("#"&MID(CELL("FILENAME",A1),FIND("]",CELL("FILENAME",A1))1,99)&&…

找出100~200的全部素数

解题思路&#xff1a; 判别 m 是否为素数的算法是这样的&#xff1a;让 m 被2~除&#xff0c;如果 m 不能被2~之中任何一个整数整除&#xff0c;就可以确定 m 是素数。为了记录 m 是否为素数&#xff0c;可以用一个布尔变量 prime 来表示。在循环开始时先设 prime 为真…

品鉴中的精神内涵:如何通过红酒品味生活的美好与哲学

红酒不仅仅是一种物质享受&#xff0c;更是一种精神体验。在品鉴云仓酒庄雷盛红酒的过程中&#xff0c;我们能够品味到生活的美好与哲学&#xff0c;感受到红酒所蕴含的精神内涵。 红酒的精神内涵源于其酿造过程中所融入的时间和匠心。一瓶上好的红酒需要经过长时间的陈年&…

重学java 34.API 5.工具类

有失才有悟&#xff0c;崩塌后的重建只会更牢固 —— 24.5.9 一、System类 1.概述: 系统相关类,是一个工具类 2.特点: a.构造私有,不能利用构造方法new对象 b.方法都是静态的 3.使用: 类名直接调用 4.方法 方法 …

vscode中配置 leetcode 插件

1. 环境准备 插件安装介绍 介绍 VS Code 1.23.0 Node.js 10 注意&#xff1a;请确保Node在PATH环境变量中。您也可以通过设定 leetcode.nodePath 选项来指定 Node.js 可执行文件的路径。 1.1 Node.js 安装 首先&#xff0c;您需要解压下载的 .tar.xz 文件。您可以使用以下…

汇编--栈和寄存器

栈 栈是一种运算受限的线性表&#xff0c;其限定仅在表尾进行插入和删除操作的线性表&#xff0c;表尾也被叫做栈顶。简单概括就是我们对于元素的操作只能够在栈顶进行&#xff0c;也造就了其先进后出的结构特性。 栈 这种内存空间其实本质上有两种操作&#xff1a;将数据放入…

Python自动化测试五大框架(测试员收藏夹必备)

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

知从科技战略客户经理张志强受邀出席2024 AutoSec中国汽车网络安全与数据安全峰会

4月11-12日&#xff0c;AutoSec8周年年会暨中国汽车网络安全及数据安全合规峰会在上海成功举办。此次峰会吸引了来自全球各地的头部汽车网络安全企业、OEM厂商、安全专家和学者等齐聚盛会&#xff0c;零距离共话智能网联汽车产业的新发展、新趋势。 知从科技董事长成云霞亲自带…

番外篇 | YOLOv8改进之在C2f中引入MSBlock模块(来源于YOLO-MS) | 轻量化网络结构

前言:Hello大家好,我是小哥谈。YOLO-MS是一种基于YOLO(You Only Look Once)的目标检测算法,它利用多尺度特征图提取和融合的方式来检测不同尺度的物体。YOLO-MS在准确率和速度方面都有很好的表现,特别适用于实时场景下的物体检测。在YOLO-MS提出的一种针对于实时目标检测…

【intro】GraphSAGE

论文 https://arxiv.org/pdf/1706.02216 abstract 大图中节点的低维embedding已经被证明在各种预测任务中非常有用&#xff0c;然而&#xff0c;大多数现有的方法要求在embedding训练期间图中的所有节点都存在;这些先前的方法属于直推式&#xff08;transductive&#xff09…

Vue2——前端笔记

Vue 一、Vue核心1.1、vue简介1.2、初始vue1.3、模板语法1.4、数据绑定1.5、el与data的两种写法1.6、MVVM模型1.7、Vue中的数据代理1.7.1、Object.defineProperty() 理解1.7.2、Vue中的数据代理 1.8、事件处理1.8.1、事件的基本用法1.8.2、事件修饰符1.8.3、键盘事件 1.9、计算属…

【递归、回溯和剪枝】二叉树中的深搜

⼆叉树中的深搜深度优先遍历&#xff08;DFS&#xff0c;全称为 Depth First Traversal&#xff09;&#xff0c;是我们树或者图这样的数据结构中常⽤的⼀种遍历算法。这个算法会尽可能深的搜索树或者图的分⽀&#xff0c;直到⼀条路径上的所有节点都被遍历完毕&#xff0c;然后…

推荐 3 个 yyds 的开源项目!

本期推荐开源项目目录&#xff1a; 1. AI 搜索引擎 2. 大模型聊天框架 3. 模仿抖音的移动端短视频 01 AI 搜索引擎 Perplexica 是一个开源的、由 AI 驱动的搜索引擎。它深入互联网寻找答案&#xff0c;不仅搜索网络&#xff0c;还理解您的问题。 Perplexica 受到 Perplexity AI…

系统安全与应用【2】

1.开关机安全控制 1.1 GRUB限制 限制更改GRUB引导参数 通常情况下在系统开机进入GRUB菜单时&#xff0c;按e键可以查看并修改GRUB引导参数&#xff0c;这对服务器是一个极大的威胁。可以为GRUB 菜单设置一个密码&#xff0c;只有提供正确的密码才被允许修改引导参数。 实例&…

YOLOv9改进策略 | 添加注意力篇 | 一文带你改进GAM、CBAM、CA、ECA等通道注意力机制和多头注意力机制

一、本文介绍 这篇文章给大家带来的改进机制是一个汇总篇&#xff0c;包含一些简单的注意力机制&#xff0c;本来一直不想发这些内容的&#xff08;网上教程太多了&#xff0c;发出来增加文章数量也没什么意义&#xff09;&#xff0c;但是群内的读者很多都问我这些机制所以单…

CUDA调整指令级原语

在GPU上运行的运算密集型应用程序&#xff0c;处理器的计算吞吐量可以用它在一段时间内执行操作的数量来衡量。因为GPU有很多SIMT指令和计算核心&#xff0c;所以其峰值计算吞吐量通常比其他的处理器高。 对应用程序的吞吐量和正确性进行优化时&#xff0c;理解不同低级原语的…