1.定义

一个拥有键值对元素的无序集合。
map 的定义如下
map[KeyType]ValueType
其中 KeyType 表示键的类型, ValueType 表示值的类型。

注意:
键的类型必须是可以用操作符==来进行比较的数据类型。
golang的映射在底层是用哈希表实现的。
map 类型的零值是nil。
map 元素不是一个变量, 不可以获取它的地址。我们无法获取map元素的地址的一个原因是map的增长可能会导致已有元素被重新散列到新的存储位置,这样就可能使得获得的地址无效。

2.创建和初始化

使用字面量来新建一个带初始化键值对元素的字典

  1. ages := map[string]int{
  2. "a": 1,
  3. "b": 2,
  4. }

使用内置函数make来创建一个map
make(map[KeyTyep]ValueType, [cap])
其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。

  1. func main() {
  2. m := make(map[string]int, 5)
  3. m["a"] = 1
  4. m["b"] = 2
  5. m["c"] = 3
  6. m["d"] = 4
  7. fmt.Printf("m: %#v\n", m)
  8. m["e"] = 5
  9. fmt.Printf("m: %#v\n", m)
  10. m["f"] = 6 // 当长度超过 cap时, 长度会增加。
  11. fmt.Printf("m: %#v\n", m)
  12. }

新的空的map,另一种表达式为 make[string]int{}

设置元素之前,必须初始化map.
map类型的零值是nil, 也就是说,没有引用任何散列表。

  1. var ages map[string] int
  2. fmt.Println(ages == nil) // true
  3. fmt.Println(len(ages) == 0) // true

大多数的map操作是都可以安全地在map的零值nil上进行, 包括查找元素, 删除元素, 获取map元素个数, 执行range循环, 因为这和空map的行为一致。但是向零值map中设置元素会导致错误:
ages["a"] = 1 // 宕机: 会报错
所以, 设置元素之前, 必须初始化map.

  1. func main() {
  2. var ages map[string]int
  3. fmt.Println(ages == nil) // true
  4. fmt.Println(len(ages) == 0) // true
  5. ages = make(map[string]int)
  6. fmt.Println(ages != nil) // true
  7. fmt.Println(len(ages) == 0) // true
  8. ages["a"] = 1
  9. fmt.Println(len(ages) == 1) // true
  10. }

查找与遍历

查找某个key 是否在map中

  1. age, ok := ages["bob"]
  2. if !ok {
  3. /*"bob" 不是字典中的键*/
  4. }

或者

  1. if age, ok := ages["bob"]; !ok {
  2. /*"bob" 不是字典中的键*/
  3. }

遍历

  1. for name, age := range ages {
  2. fmt.Printf("%s\t%d\n", name, ages)
  3. }

删除元素

使用内置函数 delete 来从字典中根据键值移除一个元素。

delete(ages, "a") // 移除元素ages["a"]
如果 “a” 这个键不存在,那么这个调用将什么都不会发生,也不会有什么副作用。但是如果传入的映射的变量的值为 nil,该调用将导致程序抛出异常(panic)。

比较

和slice 一样, map不可以比较。
唯一合法的比较是通过 nil 进行比较。
为了判断两个map是否拥有相同的键和值, 需要循环比较

  1. func equalMap(x, y map[string]int) bool {
  2. if len(x) != len(y) {
  3. return false
  4. }
  5. for xk, xv := range x {
  6. if yv, ok := y[xk]; !ok || xv != yv {
  7. return false
  8. }
  9. }
  10. return true
  11. }

集合

函数间传递映射

在函数间传递映射并不会制造出该映射的一个副本。实际上,当传递映射给一个函数,并对 这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改

  1. func moveItem(m map[string]int, key string) {
  2. delete(m, key)
  3. }
  4. func main() {
  5. m := map[string]int{
  6. "a": 1,
  7. "b": 2,
  8. "c": 3,
  9. "d": 4,
  10. }
  11. for k, v := range m{
  12. fmt.Printf("(%v, %v)\n", k, v)
  13. }
  14. fmt.Println("----------------")
  15. moveItem(m, "c")
  16. for k, v := range m{
  17. fmt.Printf("(%v, %v)\n", k, v)
  18. }
  19. }

源码解析

代码片段

参考资料

《Go in action》
《The Go Programming Language》
https://www.liwenzhou.com/posts/Go/08_map/
https://www.cnblogs.com/sparkdev/p/10749542.html