map键值对的数据结构,在go中map的key具有很大的灵活度,你可以用一切可以进行对比操作(==,!=)的类型来当key例如string,int,指针等,甚至与当你需要时你也能够用一个struct来当作key(struct内的所有变量类型都应当能够对比)。值得关注的是map是无序的当你对一个map进行增强for循环迭代时,每次的顺序都是随机的,需要额外的代码进行有序的循环(最开始增强for循环迭代map是有序的,不过出于依赖考虑被go官方改为了无序迭代)。
golang里map的初始化非常简单:
声明map:var map1 map[type]type
若不进行初始化map1默认nil,长度为0;
使用内置函数make进行map的初始化:
当key是基础类型时:
map1 := make(map[string]bool)
map1["BangBrother"] = true
map1["Chopin"] = false
fmt.Println(map1)
输出:
map[BangBrother:true Chopin:false]
当key是struct时:
type student struct {//声明struct
name string
number int64
}
s1 := student{//初始化struct
name: "1",
number: 1,
}
studentMap := make(map[student]int)
//可以直接用已存在的
studentMap[s1] = 1
//也可以新建一个
studentMap[student{
name: "2",
number: 2,
}] = 2
fmt.Println(studentMap)
输出:
map[{1 1}:1 {2 2}:2]
在student这个struct内变量不能出现数组,切片这种不能直接对比的类型,否则编译器会报错:invalid map key type 结构体名
在对比struct时是将struct内的所有变量的值一一对比一遍,有一个不同则为不同的key否则就是同一个key,跟student的引用地址无关。
s1 := student{
name: "1",
number: 1,
}
s2 := student{
name: "1",
number: 1,
}
fmt.Printf("s1:%p\n", &s1)
fmt.Printf("s2:%p\n", &s2)
studentMap := make(map[student]int)
studentMap[s1] = 1
studentMap[s2] = 2
fmt.Println(studentMap)
结果:
s1:0xc00009e440
s2:0xc00009e460
map[{1 1}:2]
可以看的出来即使是不同的student只要值相同就能被判断为相同key将其值覆盖掉。
在go语言中没有现成的循环让你获得有固定顺序的map,但是实现固定顺序循环也是非常简单的一件事。
当你只在乎里面的值而不在乎顺序时:
map1 := make(map[string]int)
map1["chopin"] = 23
map1["bang"] = 24
map1["brother"] = 25
for key, value := range map1 {
fmt.Println(key, value)
}
此时打印出来的结果顺序将是随机的
1. 2.
chopin 23 brother 25
bang 24 chopin 23
brother 25 bang 24
若想要每次程序运行时具有固定的顺序,需要额外维护一个存放有key的数据结构:
keys := make([]string, len(map1))
count := 0
for key := range map1 {
keys[count] = key
count++
}
for _, key := range keys {
fmt.Println(key, map1[key])
}
判断某个key是否存在:value, ok := map1[key]
这里的ok如果是true就代表该key存在,如果是false则不存在,且value会赋值nil。
根据key删除某个map记录可以使用内置函数delete:
delete(map1, "chopin")
此处并不需要赋值,若map1不存在该key也不会报错,无事发生。
map在并发情况下是不安全的,需要我们给与额外的支持,通常可以用sync.RWMutex这个库来解决并发的安全性问题。
读取时可以用读锁:
var lock sync.RWMutex
lock.RLock()
temp:=map1["chopin"]
lock.RUnlock()
对map设置值时可以用写锁:
lock.Lock()
map1["chopin"] = 100
lock.Unlock()
当然我们还可以直接用并发安全版本的map->sync.Map,值得注意的是sync.Map并不需要用make来进行初始化,直接调用里面的方法就好了。
var syncMap sync.Map
syncMap.Store("chopin", 123)//增加数据
syncMap.Store("bang", 100)
//为map中的每个键和值调用func(k,v interface{})bool
//当方法返回false时跳出循环,返回true继续循环
syncMap.Range(func(key interface{}, value interface{}) bool {
fmt.Println(key, value)
return true
})
fmt.Println(syncMap.Load("bang"))//通过key获取value
syncMap.Delete("chopin")//通过key删除map数据
fmt.Println(syncMap.Load("chopin"))//若不存在返回nil,false
输出结果:
chopin 123
bang 100
100 true
<nil> false
需要注意的是Range(func(k,v interface{})bool)这个函数每个key只会执行一遍,但如果同时存储或删除任意键的值,则Range可以反映Range调用期间该键的任何映射。该函数的结果顺序和增强for循环一样是随机的。
如果对更多并发操作感兴趣可以去看看别人的文档。此处仅介绍map。