#《Go 语音圣经(中文版)》

#复合数据类型

#Map

哈希表是一个无序的key/value对的集合,其中所有的key都是不同的,然后通过给定的key可以在常数时间复杂度内检索、更新或删除对应的value

Go语言中,一个map就是一个哈希表的引用,map类型可以写为map[K]V,其中KV分别对应keyvaluemap中所有的key都有相同的类型,所有的value也有着相同的类型,但是keyvalue之间可以是不同的数据类型。其中K对应的key必须是支持==比较运算符的数据类型,所以map可以通过测试key是否相等来判断是否已经存在。

内置的make函数可以创建一个map

1
ages := make(map[string]int)

也可以用map字面值的语法创建map,同时还可以指定一些最初的key/value

1
2
3
4
ages := map[string]int{
"alice": 31,
"charlie": 34,
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// -------------------------------------------
// Created By : Onns onns@onns.xyz
// File Name : 4.3.go
// Purpose :
// Creation Date : 2021-04-19 10:35:06
// Last Modified : 2021-04-19 10:36:39
// -------------------------------------------

package main

import (
"fmt"
)

func main() {
ages := make(map[string]int)
ages["alice"] = 31
ages["charlie"] = 34
fmt.Println(ages["alice"])
}

map[string]int{}可以用来创建空的map的表达式。

使用内置的delete函数可以删除元素:

1
delete(ages, "alice")

如果一个查找失败将返回value类型对应的零值。

x += yx++等简短赋值语法也可以用在map上。

map中的元素并不是一个变量,因此我们不能对map的元素进行取址操作,原因是map可能随着元素数量的增长而重新分配更大的内存空间,从而可能导致之前的地址无效。

map的迭代顺序是不确定的,并且不同的哈希函数实现可能导致不同的遍历顺序。

map类型的零值是nil,也就是没有引用任何哈希表。

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

map上的大部分操作,包括查找、删除、lenrange循环都可以安全工作在nil值的map上,它们的行为和一个空的map类似。但是向一个nil值的map存入元素将导致一个panic异常:

1
ages["carol"] = 21 // panic: assignment to entry in nil map

在向map存数据前必须先创建map

通过key作为索引下标来访问map将产生一个value。如果keymap中是存在的,那么将得到与key对应的value;如果key不存在,那么将得到value对应类型的零值。

第二个返回值用来确定对应的元素是否真的是在map之中:

1
2
3
4
5
age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }


if age, ok := ages["bob"]; !ok { /* ... */ }

在这种场景下,map的下标语法将产生两个值,第二个是一个布尔值,用于报告元素是否真的存在。布尔变量一般命名为ok,特别适合马上用于if条件判断部分。

map之间不能进行相等比较,唯一的例外是和nil进行比较。

有时候我们需要一个mapsetkeyslice类型,但是mapkey必须是可比较的类型,但是slice并不满足这个条件。不过,我们可以通过两个步骤绕过这个限制。第一步,定义一个辅助函数k,将slice转为map对应的string类型的key,确保只有xy相等时k(x) == k(y)才成立。然后创建一个keystring类型的map,在每次对map操作时先用k辅助函数将slice转化为string类型。

mapvalue类型也可以是一个聚合类型,比如是一个mapslice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// -------------------------------------------
// Created By : Onns onns@onns.xyz
// File Name : 4.4.go
// Purpose :
// Creation Date : 2021-04-19 10:59:57
// Last Modified : 2021-04-19 11:05:49
// -------------------------------------------

package main

import "fmt"

var graph = make(map[string]map[string]bool)

func addEdge(from string, to string) {
edges := graph[from]
if edges == nil {
edges = make(map[string]bool)
graph[from] = edges
}
edges[to] = true
}

func hasEdge(from, to string) bool {
return graph[from][to]
}

func main() {
addEdge("a", "b")
fmt.Println("a -> b")
addEdge("c", "d")
fmt.Println("c -> d")
addEdge("a", "d")
fmt.Println("a -> d")
addEdge("d", "a")
fmt.Println("d -> a")
fmt.Println("a b ", hasEdge("a", "b"))
fmt.Println("c d ", hasEdge("c", "d"))
fmt.Println("a d ", hasEdge("a", "d"))
fmt.Println("d a ", hasEdge("d", "a"))
fmt.Println("x b ", hasEdge("x", "b"))
fmt.Println("c d ", hasEdge("c", "d"))
fmt.Println("x d ", hasEdge("x", "d"))
fmt.Println("d x ", hasEdge("d", "x"))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
[11:05:51] onns@Onns ~/Documents/code/go/go-bible/ch4 $ go run 4.4.go
a -> b
c -> d
a -> d
d -> a
a b true
c d true
a d true
d a true
x b false
c d true
x d false
d x false