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

相关推荐
NK.MainJay3 小时前
Go语言 HTTP 服务模糊测试教程
python·http·golang
每天写点bug6 小时前
golang 常用的占位符 %w, %v, %s
开发语言·后端·golang
心动雨崽6 小时前
A算法详解(go实现)
开发语言·算法·golang
__AtYou__11 小时前
Golang | Leetcode Golang题解之第557题反转字符串中的单词III
leetcode·golang·题解
程序猿-瑞瑞13 小时前
11 go语言(golang) - 数据类型:结构体
开发语言·golang
宋发元15 小时前
Go 数据库查询与结构体映射
数据库·oracle·golang
清北_15 小时前
Go常见框架对比
前端·golang·gin
老大白菜17 小时前
goframe开发一个企业网站 验证码17
运维·docker·容器·golang·goframe
杜杜的man18 小时前
【go从零单排】Stateful Goroutines(有状态的 goroutines)
开发语言·后端·golang
Chrikk18 小时前
Go-一文打尽基础语法实例
开发语言·后端·golang