Go语言map

map 概念

在Go语言中,map 是一种内建的数据结构,它提供了一种关联式的存储机制,允许你以键值对的形式存储数据。每个键都是唯一的,并且与一个值相关联。你可以通过键来查找、添加、更新和删除值,这类似于其他编程语言中的“字典”或“关联数组”。

Go语言中的 map 具备以下特性:

  1. 无序性map 中的键值对存储顺序并不固定,每次迭代可能会得到不同的顺序(虽然从Go 1.8开始,内部实现努力保证迭代顺序的一致性,但这并不是语言规范所保证的)。

  2. 动态大小map 的容量会在需要时自动增长,不需要预先指定大小。

  3. 键值对:键和值可以是任何类型的,但键必须是可比较的,也就是说,它不能是诸如slice、map或函数这样的不可比较类型。通常情况下,整型、浮点型、字符串、指针和结构体(其中所有字段也是可比较的)都可以作为键。

  4. 初始化map 是引用类型,所以在使用前必须初始化。可以通过 make 函数来创建一个新的空 map,例如 m := make(map[keyType]valueType)。另外,从Go 1.11开始,也可以使用类型推断来初始化 map,例如 m := map[string]int{}

  5. 操作

    • 插入m[key] = value,如果键已经存在,则更新其值;否则插入新的键值对。
    • 查找value := m[key],若键存在则获取其值,否则返回该类型的零值。
    • 删除delete(m, key),用于删除给定键对应的键值对。
    • 判断键是否存在value, ok := m[key],这里的 ok 是一个布尔值,如果键存在于 map 中,则 ok 为 true,并且返回对应的值;否则 ok 为 false,返回值为该类型的零值。
  6. 迭代:可以使用 for key, value := range m {...} 循环来遍历 map 中的所有键值对。

Go语言的 map 实现上通常采用哈希表(Hash Table),因此对于大多数操作具有接近O(1)的平均时间复杂度。需要注意的是,由于并发访问可能导致数据不一致,所以在多线程环境下,如果不采取适当的同步措施,直接操作 map 可能会引发数据竞争,这时应使用 sync.Map 或使用互斥锁来保护 map 的访问。

示例:

	//var mapname map[keytype]valuetype/*其中:mapname 为 map 的变量名。keytype 为键类型。valuetype 是键对应的值类型。*/var map1 map[string]intmap2 := map[string]int{}map3 := map[string]int{"one": 1, "two": 2}map4 := make(map[string]float32)map1 = map[string]int{"one": 1, "two": 2}map2["key1"] = 6map3["one"] = 2map4["key1"] = 3.1415926map5 := map4map5["tow"] = 5   //map是引用类型,此时map4的tow的值也变成5fmt.Println("map1:", map1) //map1: map[one:1 two:2]fmt.Println("map2:", map2) //map2: map[key1:6]fmt.Println("map3:", map3) //map3: map[one:2 two:2]fmt.Println("map4:", map4) //map4: map[key1:3.1415925 tow:5]fmt.Println("map5:", map5) //map5: map[key1:3.1415925 tow:5]

注意:如果在声明一个map并赋值,采用的是如下的方式运行的时候会报错:

var map1 map[string]int 
map1["key1"] = 5

原因是:当你声明一个map变量但未初始化它时(就像 var map1 map[string]int 这样),它的值是 nil,而不是一个空的map。尝试向一个 nil map中添加键值对会导致 panic。

map 容量

和数组不同,map 可以根据新增的 key-value 动态的伸缩,因此它不存在固定长度或者最大限制,但是也可以选择标明 map 的初始容量 capacity,格式如下:

make(map[keytype]valuetype, cap)

例如:

map2 := make(map[string]float, 100)

当 map 增长到容量上限的时候,如果再增加新的 key-value,map 的大小会自动加 1,所以出于性能的考虑,对于大的 map 或者会快速扩张的 map,即使只是大概知道容量,也最好先标明。

用切片作为 map 的值

既然一个 key 只能对应一个 value,而 value 又是一个原始类型,那么如果一个 key 要对应多个值怎么办?例如,当我们要处理 unix 机器上的所有进程,以父进程(pid 为整形)作为 key,所有的子进程(以所有子进程的 pid 组成的切片)作为 value。通过将 value 定义为 []int 类型或者其他类型的切片,就可以优雅的解决这个问题,示例代码如下所示:

mp1 := make(map[int][]int)slice1 := []int{1, 2, 3}mp1[0] = slice1mp2 := make(map[int]*[]int)slice2 := []int{4, 5, 6}mp2[1] = &slice2// 现在你可以通过键来访问相应的切片
fmt.Println(mp1[0])  // 输出: [1 2 3]
fmt.Println(*mp2[1]) // 输出: [4 5 6]

遍历map

instances := map[string]int{"key1": 1, "key2": 2, "key3": 3}
for k, v := range instances {fmt.Println(k, v)
}//运行结果
//key3 3
//key1 1
//key2 2//如果只遍历值,可以使用如下方式
for _, v := range instances {fmt.Println(v)
}//运行结果
//1
//2
//3//如果只遍历键,可以使用如下方式
for k := range instances {fmt.Println(k)
}//运行结果
//key2
//key3
//key1

注意:遍历输出元素的顺序与填充顺序无关,不能期望 map 在遍历时返回某种期望顺序的结果。

如果需要特定顺序的遍历结果,正确的做法是先排序,代码如下:

scene := make(map[string]int)
// 准备map数据
scene["route"] = 66
scene["brazil"] = 4
scene["china"] = 960
// 声明一个切片保存map数据
var sceneList []string
// 将map数据遍历复制到切片中
for k := range scene {sceneList = append(sceneList, k)
}
// 对切片进行排序
sort.Strings(sceneList) 
// 输出
fmt.Println(sceneList) // //[brazil china route]//按值排序
for _, v := range scene {sceneList = append(sceneList, v)
}
// 对切片进行排序
sort.Ints(sceneList)
// 输出
fmt.Println(sceneList) //[4 66 960]

删除map元素

使用 delete() 内建函数从 map 中删除一组键值对,delete() 函数的格式如下:

delete(map, 键)

其中 map 为要删除的 map 实例,键为要删除的 map 中键值对的键。
从 map 中删除一组键值对可以通过下面的代码来完成:

map1 := make(map[string]int)// 准备map数据
map1["key1"] = 66
map1["key2"] = 4
map1["key3"] = 960delete(map1, "key1")for k, v := range map1 {
fmt.Println(k, v) 
}//key2 4
//key3 960

清空map所有元素

要清空一个Go语言中的map中的所有元素,无需逐个删除键值对,可以直接使用 make 函数重新分配一个相同类型的空映射,原映射占用的内存空间会由垃圾回收器自动回收。以下是清空映射的简单方法:

originalMap := map[string]int{"apple": 1,"banana": 2,// 更多键值对...
}// 清空映射
originalMap = make(map[string]int)fmt.Println(originalMap)  // 输出:map[]

sync.map 

sync.Map 是 Go 语言标准库 sync 包中提供的一个并发安全的映射(map)类型,特别适合在多个 Goroutine 并发读写数据的场景中使用。普通的 Go 语言 map 不支持并发安全的读写操作,如果在多线程环境中不加以同步控制,可能会导致数据竞争和不可预测的行为。

为什么要用 sync.Map:

  1. 并发安全:在高并发场景下,多个 Goroutine 同时读写 map 时,sync.Map 能够确保读写操作的线程安全性,避免因并发操作导致的数据不一致问题。
  2. 性能优化sync.Map 内部采用读写分离的设计,利用两个底层的 map 结构 (read 和 dirty) 来达到更高的并发性能。对于只读操作,sync.Map 优先从只读 map 中获取数据,只有在写操作时才会加锁,从而降低了锁竞争的概率,提高了并发性能。


下面来看下并发情况下读写 map 时会出现的问题,代码如下:

// 创建一个int到int的映射
m := make(map[int]int)
// 开启一段并发代码
go func() {// 不停地对map进行写入for {m[1] = 1}
}()
// 开启一段并发代码
go func() {// 不停地对map进行读取for {_ = m[1]}
}()
// 无限循环, 让并发程序在后台执行
for {
}

运行代码会报错,输出如下:

fatal error: concurrent map read and map write

错误信息显示,并发的 map 读和 map 写,也就是说使用了两个并发函数不断地对 map 进行读和写而发生了竞态问题,map 内部会对这种并发操作进行检查并提前发现。

需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,sync.Map 和 map 不同,不是以语言原生形态提供,而是在 sync 包下的特殊结构。

sync.Map 有以下特性:

  • 无须初始化,直接声明即可。
  • sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用,Store 表示存储,Load 表示获取,Delete 表示删除。
  • 使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。

如何使用 sync.Map:

import ("fmt""sync"
)func main() {// 创建一个 sync.Mapm := new(sync.Map)// 存储键值对m.Store("key1", "value1")m.Store("key2", "value2")// 读取值,value, ok 是两个返回值,ok 表示键是否存在value, ok := m.Load("key1")if ok {fmt.Println("Found value:", value.(string)) // 类型断言为 string 类型}// 删除键值对m.Delete("key1")// 遍历 sync.Mapm.Range(func(key, value interface{}) bool {fmt.Println("Key:", key, "Value:", value)return true // 返回 true 继续遍历,返回 false 停止遍历})
}

参考文章:

Go语言map(Go语言映射) (biancheng.net)

Go语言遍历map(访问map中的每一个键值对) (biancheng.net)

Go语言sync.Map(在并发环境中使用的map) (biancheng.net)

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

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

相关文章

Android 音视频播放器 Demo(二)—— 音频解码与音视频同步

音视频编解码系列目录: Android 音视频基础知识 Android 音视频播放器 Demo(一)—— 视频解码与渲染 Android 音视频播放器 Demo(二)—— 音频解码与音视频同步 RTMP 直播推流 Demo(一)—— 项目…

Qt+Ubuntu20.04:打包qt

打包程序 参考 qt项目在Linux平台上面发布成可执行程序.run_qt.run不是虚拟机的配置文件-CSDN博客 Linux下Qt程序的打包发布(1)-不使用第三方工具 - 知乎 (zhihu.com) 过程 1、Release编译 先将你的程序在release下编译通过,保证下面打包的程序是你最新的。 2…

C#调用skiasharp操作并绘制图片

之前学习ViewFaceCore时采用Panel控件和GDI将图片及识别出的人脸方框和关键点绘制出来,本文将其修改为基于SKControl和SKCanvas实现相同的显示效果并支持保存为本地图片。   新建Winform项目,在Nuget包管理器中搜索并安装一下SkiaSharp和ViewFaceCore…

HTTP 多个版本

了解一下各个版本的HTTP。 上个世纪90年代初期,蒂姆伯纳斯-李(Tim Berners-Lee)及其 CERN的团队共同努力,制定了互联网的基础,定义了互联网的四个构建模块: 超文本文档格式(HTML) …

Linux基础——Linux开发工具(上)_vim

前言:在了解完Linux基本指令和Linux权限后,我们有了足够了能力来学习后面的内容,但是在真正进入Linux之前,我们还得要学会使用Linux中的几个开发工具。而我们主要介绍的是以下几个: yum, vim, gcc / g, gdb, make / ma…

【初识Redis】

初识Redis Redis(Remote Dictionary Server)是一个开源的内存数据库,它提供了一个高性能的键值存储系统,并且支持多种数据结构,包括字符串、哈希、列表、集合和有序集合等。Redis的特点包括: 内存存储&…

C语言实验-数组、字符串以及指针

一&#xff1a; 求一个NN矩阵主、次对角线上所有元素之和。矩阵输入、矩阵输出、矩阵对角线求和分别用三个子函数实现。&#xff08;N的值由用户从键盘输入&#xff09; #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h>void print(int(*arr…

有哪些好用的局域网电脑监控系统软件?

企业员工不好管理&#xff1f;&#xff1f;&#xff1f; 局域网已成为企业日常运营不可或缺的一部分。 然而&#xff0c;随着网络技术的普及&#xff0c;员工在局域网中的不当行为也日益增多&#xff0c;如滥用网络资源、泄露敏感信息、消极怠工等。 为了解决这些问题&#x…

植物大战僵尸杂交版

1.感谢作者潜艇伟伟迷 2.大小大概110M&#xff0c;下载链接在下方 链接&#xff1a;https://pan.baidu.com/s/1Ew6iTg0_d_Ut8N9_18KGLw 提取码&#xff1a;yspa 3.祝大家玩的开心

C++-10

1.C一个程序&#xff0c;实现两个类&#xff0c;分别存放输入的字符串中的数字和字母&#xff0c;并按各自的顺序排列&#xff0c; 类中实现-一个dump函数&#xff0c;调C用后输出类中当前存放的字符串结果。 例如&#xff0c;输入1u4y2a3d,输出:存放字母的类&#xff0c;输出a…

树,二叉树的基本概念介绍,二叉树的性质

目录 树 树的定义 树的相关概念 树的存储结构 树在实际中的运用&#xff08;表示文件系统的目录树结构 &#xff09; 二叉树 二叉树的定义 现实中的二叉树 二叉树的特点 特殊的二叉树 1.斜树 2.满二叉树 3.完全二叉树 二叉树的性质 性质1&#xff1a;二叉树的第…

网络基础(1)网络编程套接字UDP

要完成网络编程首先要理解原IP和目的IP&#xff0c;这在上一节已经说明了。 也就是一台主机要进行通信必须要具有原IP和目的IP地址。 端口号 首先要知道进行网络通信的目的是要将信息从A主机送到B主机吗&#xff1f; 很显然不仅仅是。 例如唐僧要去到西天取真经&#xff0…

源码篇--Nacos服务--中章(8):Nacos服务端感知客户端实例变更-3

文章目录 前言一、客户端实例变更&#xff1a;二、实例变更感知&#xff1a;2.1 实例注册信息通知&#xff1a;2.1.1 接收DistroDataRequest 请求&#xff1a;2.1.2 onReceive 处理请求&#xff1a;2.1.3 processData 处理请求&#xff1a;2.1.4 handlerClientSyncData 处理数据…

k8s集群Grafana精选dashboard页面

文章目录 参考文档 Grafana自选模板推荐模板&#xff1a;13332、13824、14518Grafana默认配置我们选择 Node Exporter/Nodes 的 Dashboard 进去&#xff1a;点击 Kubernetes/Networking/Cluster 进去使用模板查看结果 Grafana接入Prometheus数据Grafana添加监控模板导入 1860_r…

体验用AI写代码

近两年&#xff0c;AI确实迎来了大爆发&#xff0c;2023年也成了AI时代的元年&#xff0c;去年下半年的时候&#xff0c;国内月之暗面出品的Kimi成了新的AI热门&#xff0c;也体验用Kimi修改论文&#xff0c;以及用图片生成代码&#xff0c;代码准确度还是蛮高的。做为SRE&…

使用Python的Tkinter库创建你的第一个桌面应用程序

文章目录 准备工作创建窗口和按钮代码解释运行你的应用程序结论 在本教程中&#xff0c;我们将介绍如何使用Python的Tkinter库创建一个简单的桌面应用程序。我们将会创建一个包含一个按钮的窗口&#xff0c;点击按钮时会在窗口上显示一条消息。 准备工作 首先&#xff0c;确保…

扩展大型视觉-语言模型的视觉词汇:Vary 方法

在人工智能领域&#xff0c;大型视觉-语言模型&#xff08;LVLMs&#xff09;正变得越来越重要&#xff0c;它们能够处理多种视觉和语言任务&#xff0c;如视觉问答&#xff08;VQA&#xff09;、图像字幕生成和光学字符识别&#xff08;OCR&#xff09;。然而&#xff0c;现有…

Docker--compose概述与部署

目录 一、概述 1. Compose简介 1.1 docker compose常用命令 1.2 Compose配置常用字段 2. YAML简介 2.1 YAML支持的数据结构 2.2 YML文件编写注意事项 2.3 Docker Compose文件结构 3. Docker-Compose安装 ​编辑 4.docker Compose撰写nginx 镜像 1. 准备环境 ​编辑…

Zabbix 安装部署说明文档

Zabbix是一个开源的网络监控和管理系统&#xff0c;其架构设计用于提供企业级的监控解决方案。以下是Zabbix的主要组件&#xff1a; 1.Zabbix Server&#xff1a;这是Zabbix系统的核心组件&#xff0c;负责接收Agent程序报告的系统可用性、系统完整性和统计数据。Zabbix Serve…

CSS 06

精灵图 为什么要使用精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度,因此&#xff0c;为了有效地减少服务…