1、map的定义
- 是由Go编译器和运行时联合实现的复合数据类型
- 无序的键值对(Key-value),Key要保证其唯一性
- key的类型必须支持"=="和"!="
- 在go中,map类型本身,切片类型,函数类型只支持与nil的比较。所以这些类型的变量都无法被当成key使用
2、map的声明和初始化
- 如果没有显示的给map变量赋值,则map类型变量的默认值为nil
- 初值为nil的切片类型变量,可用通过append内置函数进行赋值,这叫做零值可用。很遗憾,map是零值不可用
func TestMap(t *testing.T) {
// 切片是零值可用
var s1 []string
fmt.Println(s1 == nil) // true
s1 = append(s1, "a")
fmt.Println(s1, s1 == nil) // [a] false
// map是零值不可用
var m2 map[string]int
fmt.Println(m2 == nil) // true
m2["a"] = 1
fmt.Println(m2 == nil) // panic: assignment to entry in nil map [recovered]
}
问题来了,既然map是零值不可用,那我们就需要显示的去赋值。
那么map应该如何去显示赋值呢?
2.1、使用复合字面值初始化 map 类型变量
虽然用复合字面值初始化的map里面也没有键值对,但是它也不是nil了。
func TestInitMap(t *testing.T) {
// 使用复合字面值去初始化
var m1 = map[string]int{}
fmt.Println(m1) // map[]
fmt.Println(m1 == nil) // false
m1["a"] = 3
fmt.Println(m1) // map[a:3] 没有panic
// 没有初始化
var m2 map[string]int
fmt.Println(m2) // map[]
fmt.Println(m2 == nil) // true
}
2.2、使用make为map初始化
make为slice、channel、map分配内存并初始化。
3、从map中获取元素
如果key不存在,会获取value类型的零值
func TestGetValue(t *testing.T) {
var m1 = map[string]int{}
m1["a"] = 1
fmt.Println(m1["b"]) // int类型的零值 0
}
使用comma,ok 来判断是否存在某个key,以及如果存在的话,对应的value
4、从map中删除元素
func TestDelete(t *testing.T) {
var m1 = map[string]int{}
m1["a"] = 1
delete(m1, "a") // map[]
fmt.Println(m1)
}
5、map变量的传递开销
和切片类型一样,map 也是引用类型。这就意味着 map 类型变量作为参数被传递给函数或方法的时候,实质上传递的只是一个“描述符”,而不是整个 map 的数据拷贝,所以这个传递的开销是固定的,而且也很小。
// 是引用类型,无需担心传递的开销,传递的是描述符
func TestMapParam(t *testing.T) {
var m1 = make(map[string]int)
foo(m1)
fmt.Println(m1) // map[key1:11 key2:12]
}
func foo(f map[string]int) {
f["key1"] = 11
f["key2"] = 12
}
6、map与并发
map的实例不是并发安全的,也不支持并发读写。