详解Go语言Map

Map的声明

  1. 使用make()函数定义Map
go 复制代码
map_name := make(map[KeyType]valueType,initialCapacity)

KeyType是键的类型,ValueType是值的类型,initialCapacity是可选参数,用于指定map的初始容量。

  1. 使用map关键字定义Map
go 复制代码
m := map[string]int {
	"apple": 1,
	"banana": 2,
}

Map的基本操作

  • 获取元素
go 复制代码
//获取键值对
v1 := m["apple"]
v2, ok := m["pear"] //如果键不存在, ok的值为false,v2的值为该类型的零值
  • 修改元素
go 复制代码
//修改键值对
m["apple"]=5
  • 获取Map的长度
go 复制代码
//获取Map的长度
len := len(m)
  • 遍历Map
go 复制代码
for k, v := range m {
	fmt.Println("%s, %d\n",k, v)
}
  • 删除元素
go 复制代码
delete(m, "banana")

注意:若该元素不存在于指定map,delete函数也不会报错,也不会有任何影响

Go-Map使用心得及注意事项

1. 不支持并发操作(非线程安全)

当多个线程操作同一个map时,会引发线程并发,并报错fatal error: concurrent map read and map write

如何解决这个问题?

1. sync.Mutex

当存在线程正在对map进行读写的时候,不允许其他线程进入map进行读写,让其在外面等待,直到正在读写的线程操作完毕。

go 复制代码
package main

import (
	"fmt"
	"sync"
)

func main() {
	// 创建一个包含键值对的map
	myMap := make(map[string]int)

	// 创建一个互斥锁
	var mu sync.Mutex

	// 开启多个goroutine并发写入map
	for i := 0; i < 10; i++ {
		go func() {
			// 对map加锁
			mu.Lock()
			defer mu.Unlock()

			// 向map中写入数据
			myMap["key"] = myMap["key"] + 1
		}()
	}

	// 等待所有goroutine执行完成
	for i := 0; i < 10; i++ {
		go func() {
			// 对map加锁
			mu.Lock()
			defer mu.Unlock()

			// 从map中读取数据
			fmt.Println(myMap["key"])
		}()
	}

	// 等待一段时间,确保所有goroutine执行完成
	time.Sleep(time.Second)
}

线程的每次对map进行读写前对map进行加锁,当操作完成后解锁,这样就避免了并发问题。

缺点:读写性能低。每次进行读写操作都需要对整个map进行加锁,锁的颗粒度相当大。如果出现两个线程操作的map的不同区域,互不影响,完全可以将锁的颗粒度减少为线程当前需要操作的区域,这样就能大大提高读写效率。

2. sync.Map

在 Golang 中,sync.Map 是一个并发安全的 Map 实现,可以在多个 Goroutine 中安全地读写 Map。

go 复制代码
package main

import (
	"fmt"
	"sync"
)

func main() {
	m := sync.Map{}

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		m.Store("key1", "value1")
	}()

	go func() {
		defer wg.Done()
		value, _ := m.Load("key1")
		fmt.Printf("Value of key1: %v\n", value)
	}()

	wg.Wait()
}

缺点:

  1. 使用较为复杂。value类型是一个interface{},需要断言使用。
  2. 适用读多写少的场景。读写分离的实现方式,在读多写少的场景性能优越,但是写多的场景中,读操作命中率低,需要不断同步读写map,性能降低。

2. map的初始化

map的使用需要先初始化。

如果只是定义了map而没有进行初始化,对map进行写操作会报错,因为map此时为nil。

  • 写操作:报错panic: assignment to entry in nil map

但是可以进行读操作,获取某一个值,返回的是value类型的默认值。

3. key的类型

go中,map的key不支持func、slice、map这三种类型。

4. map本身为一个引用数据类型(指针)

map在程序中是一个指针类型的,传参过程中,实参和形参指的是同一个实体。

相关推荐
HotCoffee-GPS4 小时前
Golang学习笔记:定时crontab
golang
q***188410 小时前
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
开发语言·后端·golang
码上淘金1 天前
在 YAML 中如何将 JSON 对象作为字符串整体赋值?——兼谈 Go Template 中的 fromJson 使用
java·golang·json
小生凡一1 天前
图解|Go语言实现 Agent|LLM+MCP+RAG
开发语言·后端·golang
pipip.1 天前
Go原生高性能内存网关IMS,比Redis更快
开发语言·redis·golang
q***06291 天前
环境安装与配置:全面了解 Go 语言的安装与设置
开发语言·后端·golang
月屯1 天前
平台消息推送(go)
数据库·后端·golang·cocoa·iphone·gin
百锦再2 天前
第11章 泛型、trait与生命周期
android·网络·人工智能·python·golang·rust·go
hweiyu002 天前
Go Fiber 简介
开发语言·后端·golang
周杰伦_Jay2 天前
【智能体(Agent)技术深度解析】从架构到实现细节,核心是实现“感知环境→处理信息→决策行动→影响环境”的闭环
人工智能·机器学习·微服务·架构·golang·数据挖掘