Posted on 08 May 2018

映射是一种数据结构, 用于存储一系列无序的键值对, 其中所有的key都是不同的, 然后通过给定的key可以在常数时间复杂度内检索、更新或删除对应的value

映射功能强大的地方是, 能够基于键快速检索数据.

map中所有的key都有相同的类型,所有的value也有着相同的类型,但是key和value之间可以是不同的数据类型

映射的散列表包含一组桶。在存储、删除或者查找键值对的时候,所有操作都要先选择一个桶。把操作映射时指定的键传给映射的散列函数,就能选中对应的桶。这个散列函数的目的是生成一个索引,这个索引最终将键值对分布到所有可用的桶里。 随着映射存储的增加,索引分布越均匀,访问键值对的速度就越快。映射通过合理数量的桶来平衡键值对的分布。

创建和初始化

// 1. make: 未初始化, nil映射, 不能用于存储键值对, 否则,会产生一个语言运行时错误
dict := make(map[string]int)

// 2. 创建空的映射
dict := map[string]int{}

// 2. 映射字面量
dict := map[string]string {"Red":"#da1337", "Orange":"#e95a22"}

使用映射

通过声明一个未初始化的映射来创建一个值为nil的映射(称为nil映射). nil映射不能用于存储键值对, 否则会产生一个语言运行时错误.

// 赋值
colors := map[string]string{}
colors["Red"] = "#da1337"

// 1. 判断键是否存在
v, e := colors["Blue"]
if e {
    fmt.Println(v)
}
// 2. 判断键是否存在, 查找失败将返回value类型对应的零值, 这种方法只能用在映射存储的值都是非零值的情况
v, ok := colors["Blue"]
if ! ok {
    fmt.Println(v)
}

// 3. 删除, 就是用内置的delete函数
delete(colors, "Blue")

在函数间传递映射

在函数间传递映射并不会制造出该映射的一个副本.

实际上,当传递映射给一个函数,并对这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改.这个特性和切片类似,保证可以用很小的成本来复制映射。

  1. map中的元素并不是一个变量,因此我们不能对map的元素进行取址操作
  2. 在Go语言里, 通过键来索引映射时, 即便这个键不存在也总会返回一个值. 在这种情况下, 返回的是该值对应的类型的零值.
  3. 禁止对map元素取址的原因是map可能随着元素数量的增长而重新分配更大的内存空间,从而可能导致之前的地址无效。
  4. Map的迭代顺序是不确定的,并且不同的哈希函数实现可能导致不同的遍历顺序。如果要按顺序遍历key/value对,我们必须显式地对key进行排序
  5. map类型的零值是nil,也就是没有引用任何哈希表
  6. 在向map存数据前必须先创建map. 向一个nil值的map存入元素将导致一个panic异常
  7. 通过key作为索引下标来访问map将产生一个value。如果key在map中是存在的,那么将得到与key对应的value;如果key不存在,那么将得到value对应类型的零值
  8. map之间也不能进行相等比较, 唯一的例外是和nil进行比较, 要判断两个map是否包含相同的key和value,我们必须通过一个循环实现