Golang入门笔记-CH06-Map

声明和初始化 Map#

map 是一种特殊的数据结构,通过键 key 和值 value 来保存数据,可以快速地根据 key 找到其对应的 value,与 python 中的字典和 Java 中的 HashMap 类似。

map引用类型,声明方式如下:

var variable map[keyType]valueType

在声明的时候不需要知道 map 的长度,map 是可以动态增长的。

未初始化的 map 的值是 nil

map 中的 key 只能是可以用 ==!= 来比较的变量类型,例如 stringintfloat 等,不能是数组,结构体和切片,但指针和接口可以作为 key。如果要使用结构体来作为 key,需要提供 Key()Hash() 方法,value 可以为任意类型。

可以通过赋值符号来设置 key1 对应的 value

map1[key1] = value

也可以根据赋值符号获取 key1 对应的 value,若 map1 中没有 key1 对应的值,val 将会被赋值 map1 的值类型的空值。

val := map1[key1]

示例:

package main
import "fmt"

func main() {
    var mapLit map[string]int
    var mapAssigned map[string]int

    mapLit = map[string]int{"one": 1, "two": 2}
    mapCreated := make(map[string]float32)
    mapAssigned = mapLit

    mapCreated["key1"] = 4.5
    mapCreated["key2"] = 3.14159
    mapAssigned["two"] = 3

    fmt.Printf("Map literal at \"one\" is: %d\n", mapLit["one"])
    fmt.Printf("Map created at \"key2\" is: %f\n", mapCreated["key2"])
    fmt.Printf("Map assigned at \"two\" is: %d\n", mapLit["two"])
    fmt.Printf("Map literal at \"ten\" is: %d\n", mapLit["ten"])
}

上述代码运行结果为:

Map literal at "one" is: 1
Map created at "key2" is: 3.14159
Map assigned at "two" is: 3
Mpa literal at "ten" is: 0

可以用 {key1: val1, key2: val2} 来初始化 map

map 是引用类型,因此用 make 来分配内存。

map 的常规初始化方式:

var map1[keyType]valueType = make(map[keyType]valueType)

也可以简写为:

map1 := make(map[keyType]valueType)

mapCreated := make(map[string]float) 相当于:mapCreated := map[string]float{}

Map 容量#

map 会动态扩容,因此它不存在长度限制。但也可以在初始化 map 时指定它的初始容量,格式如下:

map1 := make(map[string]int, 100) // map1 的初始容量为 100

map 增长到最大容量时,容量会自动加 1。但对于性能要求较高的场景,若 map 中保存的数据较多或易快速扩张,建议在初始化时指定其初始容量。

判断键值对是否存在#

我们可以通过如下的格式来判断 map 中的 key 是否存在:

val, ok := map1[key]

ok 的值为 true,则表明该 key 存在,否则不存在。

若只是想判断 key 是否存在,而不需要获取 key 对应的 value,可以这样写:

_, ok := map1[key]

还可以和 if 混用:

if _, ok := map1[key]; ok {
    // ...
}

删除元素#

删除 map 中的元素,可以通过 delete 函数,如删除 map1key 对应的元素:

delete(map1, key)

即使要删除的 key 不存在,也不会报错。

遍历 Map#

可以通过 for range 循环来遍历 map

for key, value := range map1 {
    // ...
}

如果只需遍历 map 中的 value,可以用匿名变量来接收 key

for _, value := range map1 {
    // ...
}

如果只需要遍历 mapkey,可以省略 value

for key := range map1 {
    // ...
}

我们来看一个例子:

package main
import "fmt"

func main() {
    map1 := make(map[int]float32)
    map1[1] = 1.0
    map1[2] = 2.0
    map1[3] = 3.0
    map1[4] = 4.0
    for key, value := range map1 {
        fmt.Printf("key is: %d - value is: %f\n", key, value)
    }
}

上述代码运行结果为:

key is: 3 - value is: 3.000000
key is: 1 - value is: 1.000000
key is: 4 - value is: 4.000000
key is: 2 - value is: 2.000000

注意map 中元素不是按照 key 来排序的。

Map 类型的切片#

如果我们要创建一个 map 类型的切片,一定要使用两次 make() 函数,第一次分配切片的内存,第二次分配 map 的内存。

来看一个例子:

package main
import "fmt"

func main() {
    // Version A:
    items := make([]map[int]int, 5)
    for i:= range items {
        items[i] = make(map[int]int, 1)
        items[i][1] = 2
    }
    fmt.Printf("Version A: Value of items: %v\n", items)

    // Version B: NOT GOOD!
    items2 := make([]map[int]int, 5)
    for _, item := range items2 {
        item = make(map[int]int, 1) // item 仅仅是切片 items2 元素的拷贝
        item[1] = 2
    }
    fmt.Printf("Version B: Value of items: %v\n", items2)
}

运行结果为:

Version A: Value of items: [map[1:2] map[1:2] map[1:2] map[1:2] map[1:2]]
Version B: Value of items: [map[] map[] map[] map[] map[]]