Go指南5-map使用的几个问题

304 阅读2分钟

前言

本文主要讲述使用map可能会遇到的几个问题,希望能帮到大家!

1.创建map的两种方式

func main() {
// 1.直接声明并初始化
	map1 := map[string]int{"aa": 1, "bb": 2}
	map1["cc"] = 3
  fmt.Println(map1)  // [aa:1 bb:2 cc:3]
  
// 2.通过make关键字
	map2 := make(map[string]int)
	map2["aa"] = 1
	fmt.Println(map2)
}

可能会有人奇怪为什么通过var map1 map[string]int 声明一个map后,无法添加数据。

这是因为var关键字只声明了map,并没有初始化它,相当于创建了一个指针(即分配了指针的内存空间),但是没有分配指针指向的空间(存储数据的空间),而make关键字就是两个都分配。

2.map结构体成员不能修改问题

2020-03-29-16-22-51.png

这是因为如果你想实现赋值,即x = y,则必须知道x的内存地址。但是Golang中的map的value本身是不可寻址的,由于value不可寻址,所以像上面那样赋值就会出错。

2020-03-29-16-26-18.png 解决办法也很简单,加上指针即可,因为加上指针,value的地址就知道了,也就能赋值了。

参考:Go struct 类型的 map 结构体成员不能修改的问题

3.map的并发问题

当在单个协程中读写map时,并不会出现什么问题, 但是如果多个协程并发访问一个map,有可能会导致程序退出,并打印下面的错误信息: fatal error: concurrent map read and map write。 当并发的协程数比较大时,遇到的概率较大

下面是一个栗子:

// 这种情况下map是不安全的
func readMap(Map map[int]int, key int) int{
	fmt.Println(key)
	return Map[key]
}

func writeMap(Map map[int]int, key int, value int){
	Map[key] = value
}

func unsafeMap(){
	Map := make(map[int]int)

	for i:=0; i<1000; i++{
		// 每循环一次,创建两个goroutine线程
		go writeMap(Map, i, i)
		go readMap(Map, i)
	}
}

func main() {
	unsafeMap()
}

map的并发可以通过加锁解决

type SafeMap struct {
	sync.RWMutex
	Map map[int]int
}

func newSafeMap(size int) *SafeMap  {
	sm := new(SafeMap)
	sm.Map = make(map[int]int, size)
	return sm
}

func (sm *SafeMap) readSafeMap(key int) int  {
	// 读写锁, 读不会阻塞,写会阻塞
	sm.RLock()
	value := sm.Map[key]
	fmt.Println(value)
	sm.RUnlock()
	return value
}

func (sm *SafeMap) writeSafeMap(key int, value int)  {
	// 互斥锁, 读和写都会阻塞
	sm.Lock()
	sm.Map[key] = value
	sm.Unlock()
}

func viewSafeMap()  {
	safeMap := newSafeMap(10)
	for i := 0; i < 1000; i++{
		go safeMap.writeSafeMap(i, i)
		go safeMap.readSafeMap(i)
	}
}


func main() {
	viewSafeMap()
}

参考:go语言坑之并发访问map

4.比较两个 map 是否相等

可通过 reflect.DeepEqual 比较,适用于 slice 和 struct,详情请戳:mozillazg.com/2014/11/go-…