详解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在程序中是一个指针类型的,传参过程中,实参和形参指的是同一个实体。

相关推荐
花酒锄作田9 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
qwfys2009 天前
How to install golang 1.26.0 to Ubuntu 24.04
ubuntu·golang·install
codeejun9 天前
每日一Go-25、Go语言进阶:深入并发模式1
开发语言·后端·golang
石牌桥网管9 天前
Go 泛型(Generics)
服务器·开发语言·golang
小二·9 天前
Go 语言系统编程与云原生开发实战(第21篇)
开发语言·云原生·golang
小二·9 天前
Go 语言系统编程与云原生开发实战(第20篇)
开发语言·云原生·golang
女王大人万岁9 天前
Golang实战Eclipse Paho MQTT库:MQTT通信全解析
服务器·开发语言·后端·golang
codeejun9 天前
每日一Go-24、Go语言实战-综合项目:规划与搭建
开发语言·后端·golang
石牌桥网管9 天前
Go类型断言
开发语言·后端·golang
普通网友10 天前
PHP语言的正则表达式
开发语言·后端·golang