Maps
Q7nl1s admin

什么是map?

map 是 Go 中的内置类型,用于存储键值对。让我们以有几个员工的创业公司为例。为简单起见,我们假设所有这些员工的名字都是唯一的。我们正在寻找一种数据结构来存储每个员工的工资。map将非常适合此用例。员工的姓名可以是键,薪水可以是值。地图类似于 Python 等其他语言中的字典。

如何创建map?

可以通过将键和值的类型通过传递给make函数来创建映射。以下是创建新map的语法。

1
2
make(map[type of key]type of value)  
employeeSalary := make(map[string]int)

上面的代码行创建了一个名为employeeSalarymap,其中包含string键和int值。

1
2
3
4
5
6
7
8
9
10
package main

import (
"fmt"
)

func main() {
employeeSalary := make(map[string]int)
fmt.Println(employeeSalary)
}

上面的程序创建了一个命名为employeeSalary并且键和值的类型分别为stringint的map。上面的程序将打印:

1
map[]  

由于我们没有向地图添加任何元素,因此它是空的。

将项目添加到地图

将新项目添加到地图的语法与数组相同。下面的程序将一些新员工添加到employeeSalary地图中。

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"
)

func main() {
employeeSalary := make(map[string]int)
employeeSalary["steve"] = 12000
employeeSalary["jamie"] = 15000
employeeSalary["mike"] = 9000
fmt.Println("employeeSalary map contents:", employeeSalary)
}

我们增加了三名员工stevejamie以及mike他们相应的工资。

上面的程序打印:

1
employeeSalary map contents: map[steve:12000 jamie:15000 mike:9000]  

也可以在声明本身期间初始化映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
)

func main() {
employeeSalary := map[string]int {
"steve": 12000,
"jamie": 15000,
}
employeeSalary["mike"] = 9000
fmt.Println("employeeSalary map contents:", employeeSalary)
}

上面的程序employeeSalary在声明过程中声明并添加了两个元素。后来又添加了一个带键的元素mike。程序打印

1
employeeSalary map contents: map[jamie:15000 mike:9000 steve:12000]  

不必只有字符串类型才是键。所有可比较的类型,例如 boolean、integer、float、complex、string,… 也可以是键。即使是用户定义的类型(例如struct)也可以是键。如果您想了解更多关于可比较类型的信息,请访问http://golang.org/ref/spec#Comparison_operators

地图的零值

地图的零值是nil。如果您尝试向地图添加元素,则会发生nil运行时panic。因此,必须在添加元素之前初始化地图。

1
2
3
4
5
6
package main

func main() {
var employeeSalary map[string]int
employeeSalary["steve"] = 12000
}

在上面的程序employeeSalary中,nil我们正在尝试向地图添加一个新键。程序将因错误而panic

1
panic: assignment to entry in nil map

从映射中检索键的值

现在我们已经向地图添加了一些元素,让我们学习如何检索它们。map[key]是检索地图元素的语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
)

func main() {
employeeSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
"mike": 9000,
}
employee := "jamie"
salary := employeeSalary[employee]
fmt.Println("Salary of", employee, "is", salary)
}

上面的程序非常简单。jamie检索并打印员工的工资。程序打印

1
Salary of jamie is 15000  

如果一个元素不存在会发生什么?该映射将返回该元素类型的零值。在employeeSalarymap 的案例下,如果我们尝试访问不存在的元素,将返回int的零值0

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"
)

func main() {
employeeSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
fmt.Println("Salary of joe is", employeeSalary["joe"])
}

上述程序的输出是:

1
Salary of joe is 0  

上述程序将返回 joe 的薪水为0。当我们尝试检索地图中不存在的键的值时,不会出现运行时错误。

检查密钥是否存在

在上一节中我们了解到,当键不存在时,将返回类型的零值。当我们想要找出键是否真的存在于地图中时,这无济于事。

例如,我们想知道employeeSalary地图中是否存在某个键。

1
value, ok := map[key]  

以上是找出特定键是否存在于映射中的语法。如果ok为真,则该键存在且其值存在于变量value中,否则该键不存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
)

func main() {
employeeSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
newEmp := "joe"
value, ok := employeeSalary[newEmp]
if ok == true {
fmt.Println("Salary of", newEmp, "is", value)
return
}
fmt.Println(newEmp, "not found")

}

在上面的程序中,在第 1 行。13、ok将是假的,因为joe不存在。因此程序将打印,

1
joe not found  

遍历地图中的所有元素

for循环的range形式用于迭代地图的所有元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
)

func main() {
employeeSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
"mike": 9000,
}
fmt.Println("Contents of the map")
for key, value := range employeeSalary {
fmt.Printf("employeeSalary[%s] = %d\n", key, value)
}

}

上述程序输出,

1
2
3
4
Contents of the map  
employeeSalary[mike] = 9000
employeeSalary[steve] = 12000
employeeSalary[jamie] = 15000

一个重要的事实是,在使用时从映射中检索值的顺序for range不能保证对于程序的每次执行都是相同的。它也与将元素添加到地图的顺序不同

从地图中删除项目

delete(map, key)key是从 a中删除的语法map。删除函数不返回任何值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
)

func main() {
employeeSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
"mike": 9000,
}
fmt.Println("map before deletion", employeeSalary)
delete(employeeSalary, "steve")
fmt.Println("map after deletion", employeeSalary)

}

上面的程序删除密钥steve并打印

1
2
map before deletion map[steve:12000 jamie:15000 mike:9000]  
map after deletion map[mike:9000 jamie:15000]

如果我们尝试删除地图中不存在的键,则不会出现运行时错误。

Map of structs

到目前为止,我们只在地图中存储了员工的工资。如果我们也能够在地图中存储每个员工的国家,那不是很好吗?这可以通过使用structs的映射来实现。员工可以表示为包含字段salary 和country 的结构,它们将与字符串键和结构值一起存储在地图中。让我们编写一个程序来了解如何做到这一点。

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
package main

import (
"fmt"
)

type employee struct {
salary int
country string
}

func main() {
emp1 := employee{
salary: 12000,
country: "USA",
}
emp2 := employee{
salary: 14000,
country: "Canada",
}
emp3 := employee{
salary: 13000,
country: "India",
}
employeeInfo := map[string]employee{
"Steve": emp1,
"Jamie": emp2,
"Mike": emp3,
}

for name, info := range employeeInfo {
fmt.Printf("Employee: %s Salary:$%d Country: %s\n", name, info.salary, info.country)
}

}

在上面的程序中,employeestruct 包含字段salarycountry。我们创建了三个员工emp1emp2并且emp3

在第25行,我们用我们创建的三个员工初始化一个键类型string和值类型为employee的映射。

map在第 31 行迭代,员工详细信息打印在下一行。该程序将打印:

1
2
3
Employee: Mike Salary:$13000  Country: India  
Employee: Steve Salary:$12000 Country: USA
Employee: Jamie Salary:$14000 Country: Canada

地图长度

地图的长度可以使用len函数确定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
)

func main() {
employeeSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
fmt.Println("length is", len(employeeSalary))

}

*上面程序中的len(employeeSalary)*返回地图的长度。上面的程序打印,

1
length is 2  

地图是引用类型

与slices类似,maps 是引用类型。当映射分配给新变量时,它们都指向相同的内部数据结构。因此,在一个中所做的更改将反映在另一个中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
)

func main() {
employeeSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
"mike": 9000,
}
fmt.Println("Original employee salary", employeeSalary)
modified := employeeSalary
modified["mike"] = 18000
fmt.Println("Employee salary changed", employeeSalary)

}

在上述程序中的第 14 行,employeeSalary分配给modified。 在下一行中,mikesalary在 map 中更改为18000。mike的salary现在在employeeSalary中也为18000。程序输出:

1
2
Original employee salary map[jamie:15000 mike:9000 steve:12000]  
Employee salary changed map[jamie:15000 mike:18000 steve:12000]

当映射作为参数传递给函数时,情况也是如此。当函数内的地图发生任何变化时,调用者也可以看到它。

Maps equality

无法使用==运算符比较地图。==只能用于检查地图是否为nil

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

func main() {
map1 := map[string]int{
"one": 1,
"two": 2,
}

map2 := map1

if map1 == map2 {
}
}

上述程序将无法编译并出现错误

1
invalid operation: map1 == map2 (map can only be compared to nil)  

检查两个映射是否相等的一种方法是逐个比较每个映射的各个元素。另一种方法是使用反射。我鼓励你为此编写一个程序并让它工作:)。

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View