Map声明
-
map 类型是一个无序的键值对的集合。
map[KeyType]ValueType
显式赋值
-
使用复合字面值,Go 允许省略字面值中的元素类型
m1 := map[int][]string{ 1: []string{"val1_1", "val1_2"}, 3: []string{"val3_1", "val3_2", "val3_3"}, 7: []string{"val7_1"}, } -
使用 make 这个预声明的内置函数。
m1 := make(map[int]string) // 未指定初始容量 m2 := make(map[int]string, 8) // 指定初始容量为8 -
没有显式地赋予 map 变量初值,map 类型变量的默认值为 nil。
-
初值为零值 nil 的切片类型变量,可以借助内置的 append 的函数进行操作,这种在 Go 语言中被称为“零值可用”,但 map 类型,因为它内部实现的复杂性,无法“零值可用”。
-
在访问的 Key 不存在时,仍会返回零值,不能通过返回 nil 来判断元素是否存在
func TestMap(t *testing.T) { m := map[string]int{"one": 1, "two": 2, "three": 3} fmt.Println(m) for k, v := range m { t.Log(k, v) } if v, ok := m["four"]; ok { t.Log("four", v) } else { t.Log(v, ok) // 0 false } }
操作
-
操作一:插入新键值对。
m := make(map[int]string) m[1] = "value1" m[2] = "value2" m[3] = "value3" -
操作二:获取键值对数量。
m := map[string]int { "key1" : 1, "key2" : 2, } fmt.Println(len(m)) // 2 m["key3"] = 3 fmt.Println(len(m)) // 3 -
操作三:查找和数据读取
m := make(map[string]int) v, ok := m["key1"] if !ok { // "key1"不在map中 } // "key1"在map中,v将被赋予"key1"键对应的value -
delete(map, key)函数是从 map 中删除键的唯一方法。即便传给 delete 的键在 map 中并不存在,delete 函数的执行也不会失败,更不会抛出运行时的异常。m := map[string]int { "key1" : 1, "key2" : 2, } fmt.Println(m) // map[key1:1 key2:2] delete(m, "key2") // 删除"key2" fmt.Println(m) // map[key1:1] -
遍历 map 的键值对只有一种方法,那就是像对待切片那样通过 for range 语句对 map 数据进行遍历。程序逻辑千万不要依赖遍历 map 所得到的的元素次序。
for k, v := range m { fmt.Printf("[%d, %d] ", k, v) } -
和切片类型一样,map 也是引用类型。
map 的内部实现
- 运行时实现了 map 类型操作的所有功能,包括查找、插入、删除等。在编译阶段,Go 编译器会将 Go 语法层面的 map 操作,重写成运行时对应的函数调用。
- Go 运行时是利用 maptype 参数中的信息确定 key 的类型和大小的。
- map 不是线程安全的,不支持并发读写;
- 考虑到 map 可以自动扩容,map 中数据元素的 value 位置可能在这一过程中发生变化,所以 Go 不允许获取 map 中 value 的地址,这个约束是在编译期间就生效的。
map 与⼯厂模式
-
map 的 value 可以是⼀个方法
func TestMapFunc(t *testing.T) { m := map[int]func(op int) int{} m[1] = func(op int) int { return op * op } t.Log(m[1](3)) // 9 } -
与 Go 的 Dock type 接⼝方式⼀起,可以⽅便的实现单⼀方法对象的工厂模式
func TestSet(t *testing.T) { mySet := map[int]bool{} mySet[1] = true t.Log(len(mySet)) // 1 delete(mySet, 1) t.Log(len(mySet)) // 0 }