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

相关推荐
007php00719 分钟前
服务器上PHP环境安装与更新版本和扩展(安装PHP、Nginx、Redis、Swoole和OPcache)
运维·服务器·后端·nginx·golang·测试用例·php
祁许13 小时前
【Golang】GORM - GEN工具 快速开始
开发语言·golang
showyoui13 小时前
深入Go语言之slice:不只是动态数组
后端·golang·slice·切片
余厌厌厌13 小时前
Go迭代器完全指南:从基础到实战
开发语言·golang
C墨羽17 小时前
使用Gin框架构建高并发教练预约微服务:架构设计与实战解析
微服务·架构·golang·gin
亚洲第一中锋_哈达迪2 天前
详解缓存淘汰策略:LFU
后端·缓存·golang
nextera-void2 天前
深入浅出 Golang:一次精神之旅
开发语言·golang·go
胡萝卜的兔2 天前
golang -gorm 增删改查操作,事务操作
开发语言·后端·golang
Go Dgg2 天前
【Go + Gin 实现「双 Token」管理员登录】
开发语言·golang·gin
nbsaas-boot3 天前
Go语言生态成熟度分析:为何Go还无法像Java那样实现注解式框架?
java·开发语言·golang