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]int
	map2 := 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"] = 6
	map3["one"] = 2
	map4["key1"] = 3.1415926
	map5 := map4
	map5["tow"] = 5   //map是引用类型,此时map4的tow的值也变成5
	
	fmt.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] = slice1

mp2 := 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"] = 960

delete(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.Map
    m := 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.Map
    m.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.mfbz.cn/a/586008.html

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

相关文章

Spring Boot的热部署工具“AND”Swagger测试工具

Spring Boot的热部署&Swagger测试页面的使用 热部署指的是在项目无需重启的情况下,只需要刷新页面,即可获得已经修改的样式或功能。要注意该工具一般用于开发环境,在生产环境中最好不要添加这个工具。 对于无需重启便可刷新这么方便的工…

小剧场短剧影视小程序源码_后端PHP

项目运行截图 源码贡献 https://githubs.xyz/boot?app42 部署说明 linux/win任选 PHP版本:7.3/7.2(测试时我用的7.2要安装sg扩展 ) 批量替换域名http://video.owoii.com更换为你的 批量替换域名http://120.79.77.163:1更换为你的 这两个…

交通运输智慧监管平台---强化物流安全与效率的新举措

一、建设背景 随着社会对于交通安全和环境保护的要求不断提高,对卡车运输的监管和合规性要求也逐渐加强。为了满足快速发展的物流需求,提高供应链协同和可追溯性、解决安全问题、提高运输效率和降低成本,我们利用现代技术和信息化手段着力建设…

Spark SQL编程初级实践

参考链接 Spark编程: Spark SQL基本操作 2020.11.01_df.agg("age"->"avg")-CSDN博客 RDD编程初级实践-CSDN博客 Spark和Hadoop的安装-CSDN博客 1. Spark SQL基本操作 { "id":1 , "name":" Ella" , "age":…

数字电路-5路呼叫显示和8路抢答器

本内容涉及两个电路,分别为5路呼叫显示电路和8路抢答器电路,包含Multisim仿真原文件,为掌握FPGA做个铺垫。紫色文字是超链接,点击自动跳转至相关博文。持续更新,原创不易! 目录: 一、5路呼叫显…

每日OJ题_DFS爆搜深搜回溯剪枝②_力扣526. 优美的排列

目录 力扣526. 优美的排列 解析代码 力扣526. 优美的排列 526. 优美的排列 难度 中等 假设有从 1 到 n 的 n 个整数。用这些整数构造一个数组 perm(下标从 1 开始),只要满足下述条件 之一 ,该数组就是一个 优美的排列 &#…

nginx缓存清理

背景 昨天打开我的gpt镜像网站,意外发现静态图片资源全都无法获取了 CoCo-AI 一番排查下来,发现是引用的cdn链接失效了 且cdn源是属于七牛云的,且不再维护,于是果断切换到cloudflare export function getEmojiUrl(unified: str…

JavaScript中的Object方法、Array方法、String方法

个人主页:学习前端的小z 个人专栏:JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! 文章目录 🔥Object方法🌞1 Object.is()🌞2 Object.…

区块链 | 由外部实体导致的 NFT 安全问题

🦊原文: Understanding Security Issues in the NFT Ecosystem 🦊警告: 本文只记录了原文的第 6 节。 1 问题描述 NFT 所指向的数字资产(图片、视频等)必须是可以访问的,这样 NFT 才具有意义…

iA Writer for Mac:简洁强大的写作软件

在追求高效写作的今天,iA Writer for Mac凭借其简洁而强大的功能,成为了许多作家、记者和学生的首选工具。这款专为Mac用户打造的写作软件,以其独特的设计理念和实用功能,助你轻松打造高质量的文章。 iA Writer for Mac v7.1.2中文…

数据挖掘之基于Lightgbm等多模型消融实验的信用欺诈检测实现

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 在当前的金融环境中,信用欺诈行为日益增多,给金融机构和消费者带来了巨大的损…

ThingsBoard PE专业版解决方案技术文档——温度湿度

1、项目总览 2、设备接入 3、设备告警 3.1 高温告警 创建一个Flag作为标杆,作为开启告警的开关。 3.2 低湿度告警 创建一个Flag作为标杆,作为开启告警的开关。 4、部件仪表 4.1 Entities table 部件预览: 标题样式: {"…

nuxt3项目服务端bulid后在本地浏览的3种方式(nuxi preview、Node.js Server、PM2)

你也许会问有了开发调试本地浏览,为什么还要服务端构建之后在本地浏览? 举个简单例子 在 Nuxt 3 服务端打包中,由于运行环境不同,无法直接访问 process 对象。服务端打包通常是在 Node.js 环境中进行的,而 process 对象…

Linux 手动部署JDK21 环境

1、下载包(我下载的是tar) https://www.oracle.com/cn/java/technologies/downloads/#java21 完成后进行上传 2、检查已有JDK,并删除(我原有是jdk8) rpm -qa | grep -i java | xargs -n1 rpm -e --nodeps3、清理掉 profile中的j…

vue3 安装-使用之第一篇

首先需要node版本高于V16.14.1 安装 执行 npm create vitelatest 具体选择按照自己实际需要的来 Project name:项目名称 Select a framework:选择用哪种框架 (我选择vue) Select a variant: 选择用JS还是TS(我选择JS)找到项目&…

【云原生】Docker 实践(三):使用 Dockerfile 文件构建镜像

Docker 实践(三):使用 Dockerfile 文件构建镜像 1.使用 Dockerfile 文件构建镜像2.Dockerfile 文件详解 1.使用 Dockerfile 文件构建镜像 Dockerfile 是一个文本文件,其中包含了一条条的指令,每一条指令都用于构建镜像…

笔记-PPT绘图导出高清无失真图片

问题描述:PPT绘图已经用了高清图(jpg、tif格式),但论文图片还是不清晰,打印出来还是有点糊 以下是PPT导出高清不失真图片(emf格式)的具体描述。 目录 一、绘图工具二、操作步骤 一、绘图工具 …

Java | Leetcode Java题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; class Solution {public String getPermutation(int n, int k) {int[] factorial new int[n];factorial[0] 1;for (int i 1; i < n; i) {factorial[i] factorial[i - 1] * i;}--k;StringBuffer ans new StringBuffer();int[] valid…

为什么公共事业机构会偏爱 TiDB :TiDB 数据库在某省妇幼健康管理系统的应用

本文介绍了某省妇幼健康管理系统的建设和数据库架构优化的过程。原有的数据库架构使用了 StarRocks 作为分析层&#xff0c;但随着业务的发展&#xff0c;这套架构暴露出诸多痛点&#xff0c;不再适应妇幼业务的需求。为解决这些问题&#xff0c;该系统选择了将原有架构中的 St…

Cesium 3dTileset 支持 uv 和 纹理贴图

原理: 使用自定义shader实现uv自动计算 贴图效果: uv效果: