映射是一种键-值对类型的内置数据结构。它将键映射到值,其中键是唯一的,即不允许重复,重复则会覆盖之前的值。映射的键类型必须是完整的定义了相等运算符(==!=)的类型,如整数,浮点数,复数,字符串,指针,结构和数组;如果键是接口类型,则其动态类型必须支持相等比较。因此,键类型不能是函数,映射或切片。失败会触发运行时panic。映射中的元素是无序的。映射的零值为nilnil映射尚未初始化,不能添加元素,也不能使用。

一 映射类型

映射类型如下:
map[key类型]元素类型

map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}

映射中元素的个数称为映射的长度。与数组和切片一样,可以使用内置函数len()来获取映射的长度,映射的长度可能在执行期间发生变化。执行期间可以添加或检索其中的元素,也可以使用内置函数delete()移除其中的元素。

与切片一样,映射保存对底层数据结构的引用。若将映射传入函数中,并更改了该映射的内容,则此修改对调用者同样可见。

1.1 映射的初始化

可以使用内置函数make()来构建一个新的空映射,它可以接受一个映射类型和一个可选的初始容量参数:

package main

import "fmt"

func main() {
    var m map[string]int // 声明一个零值映射

    week := make(map[string]int) // 初始化一个map
    week["周一"] = 0               // 增加一个元素
    week["周二"] = 1

    color := make(map[string]string, 3) // 一个初始容量为3的map
    color["red"] = "红"
    color["orange"] = "橙"
    color["yellow"] = "黄"
    color["green"] = "绿"

    fmt.Println(m)                             // map[]
    fmt.Println(week)                          // map[] map[周一:0 周二:1]
    fmt.Println(color)                         // map[green:绿 orange:橙 red:红 yellow:黄]
    fmt.Println(len(m), len(week), len(color)) // 获取长度:0 2 4
    fmt.Println(m == nil)                      // true
}

初始容量并不限制其大小,映射会自动增长以容纳存储在其中的元素,nil映射除外。nil映射和空映射差不多,除了不能往里添加元素。

1.2 映射字面量

可以直接使用复合字面量语法来构造映射:

var color = map[string]string{
    "red":    "红",
    "orange": "橙",
    "yellow": "黄",
    "green":  "绿", // 注意每行结尾的逗号不能少
}

fmt.Println(color)
package main

import "fmt"

type Point struct {
    X int
    Y int
}

var mp = map[string]Point{
    "A": Point{2, 3},
    "B": Point{3, 4},
    "C": Point{4, 5},
}

func main() {
    fmt.Println(mp)
}

如果元素顶级类型有类型名,也可以直接省略:

var mp = map[string]Point{
    "A": {2, 3},
    "B": {3, 4},
    "C": {4, 5},
}

二 映射的使用

2.1 插入,修改与检索

赋值与获取键的值与数组和切片类似,不过映射的索引不需要是整数:

color["orange"] = "橙色" // 插入或修改
value := color["orange"] // 获取值
fmt.Println(value)

尝试获取映射中不存在的键对应的值,将返回映射中对应项的类型的零值:

package main

import "fmt"

func main() {

    lang := make(map[string]bool)
    lang["Java"] = true
    lang["Golang"] = true

    fmt.Println(lang["PHP"]) // false
}

2.2 逗号ok

有时需要区分某项是确实不存在还是值为零值。比如,"PHP"这个键到底是不存在,还是说存在,但值为零值,我们可以使用多重赋值来区分这种情况:

package main

import "fmt"

func main() {

    lang := make(map[string]bool)
    lang["Java"] = true
    lang["Golang"] = true
    lang["C#"] = false

    csharp, ok := lang["C#"]
    fmt.Println(csharp, ok) // false true

    php, ok := lang["PHP"]
    fmt.Println(php, ok) // false false
}

在Go中,这被称为“逗号ok”用法。这样,我们就可以通过ok变量看出一个键到底存不存在:
如果"C#"存在,csharp将被分配对应的值,并且ok将为true;如果不存在,csharp将被设置为零值,ok将为false。可以对比一下csharpphp两个变量的结果。

只测试映射中一个键是否存在而不关心实际的值的话,可以使用空白标识符(_)来丢弃第一个变量:

_, ok := lang["PHP"]

2.3 映射的迭代

同迭代数组和切片一样,映射可以使用for-range循环来迭代:

for k, v := range lang {
    fmt.Printf("%v = %v\n", k, v)
}

2.4 删除元素

要删除映射中一个条目的话,使用delete内建函数,它的参数为映射和要删除的key。即使映射中已经没有该key,执行删除也是安全的。

delete(lang, "PHP")

参考:
https://golang.org/doc/effective_go.html#maps
https://golang.org/ref/spec#Map_types

文章目录