go中map的学习记录

90 阅读2分钟

1、map的定义

  • 是由Go编译器和运行时联合实现的复合数据类型
  • 无序的键值对(Key-value),Key要保证其唯一性
  • key的类型必须支持"=="和"!="
  • 在go中,map类型本身,切片类型,函数类型只支持与nil的比较。所以这些类型的变量都无法被当成key使用

image.png

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的实例不是并发安全的,也不支持并发读写。