映射

映射 属于 Go

2 个练习

关于 映射

在 Go 中,map是一种内置的数据类型,用于表示哈希表。在其他编程语言中,你可能把 map 称作 dict,或者关联数组,或者键值存储。如果你还不熟悉这个概念,可以把 map想象成一本字典:里面的每个单词就是 key,而释义则是这个 map 的 element。

在开始之前,我想先向你推荐关于 map 的 Go 语言规范和关于 map 的 Go 博客,方便你更深入地了解 map。

从语法上看,map长这样:

map[KeyType]ElementType

KeyType必须是任何可比较类型,而 ElementType可以是 Go 中任何合法的类型,这意味着你可以存放从基本类型的变量到切片在内的任何东西。

有一点要记住:Go 中的map是无序的。如果你试着遍历一个 map 并打印其中的元素,可能会惊讶地发现元素是以随机顺序打印出来的(不妨自己试试)。

同样重要的是,每个键都是唯一的,也就是说,对同一个键赋值两次,会覆盖该键对应的值。

map是引用类型,这意味着如果你到处传递它,Go 不会复制整个 map。Go 所做的只是复制这个 map 的指针,这使得把 map 传给函数或变量的开销很低。未初始化的 map 的值是 nil。

你可以像下面这样定义一个 map(我们也把它称作 nil map):

  var foo map[string]int

要初始化一个 map,可以这样做:

  // With map literal
  foo := map[string]int{}

或者

  // or with make function
  foo := make(map[string]int)

nil map 与已初始化的 map 不同,向 nil map 写入会导致运行时错误

panic: assignment to entry in nil map

因此,在使用 map 之前先初始化它很重要

下面是一些你可以对 map 执行的操作

  // Add a value in a map with the `=` operator:
  foo["bar"] = 42
  // Here we update the element of `bar`
  foo["bar"] = 73
  // To retrieve a map value, you can use
  baz := foo["bar"]
  // To delete an item from a map, you can use
  delete(foo, "bar")

如果你尝试获取 map 中某个不存在的键的值,它会返回该值类型的零值。这可能会让你感到困惑,尤其是当你的ElementType的默认值(例如 int 的 0)本身就是一个合法值时。要检查某个键是否存在于 map 中,可以使用

  value, exists := foo["baz"]
  // If the key "baz" does not exist,
  // value: 0; exists: false

正如前面看到的,判断一个map是否已初始化很容易,直接把它和 nil 比较即可。但如果是其他 map 呢?由于map不是可比较类型,我们不能使用==运算符,不过 reflect 包有办法帮我们,它叫做 DeepEqual,用法如下

import "reflect"

// ....

equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true

不过等一下,既然 map 不是可比较类型,为什么我们还能把它和 nil 比较呢?其实规范为此开了个特例,参见可比较类型规范。

最后一点,如果你试图从多个 goroutine 向一个map写入,会触发竞态检测器,参见 Go 文档 和 Go 博客。你也可以改用 sync.Map、atomic 或 mutex 来绕过这个问题。

通过 GitHub 编辑 该链接会在新窗口或标签页中打开

学习 映射

练习已锁定

再解锁 3 个练习即可练习 映射