大多数现代编程语言都有一个字典或一个 hash 类型的概念,这些类型通常用来将数据存储在对中,使用一个 ** 密钥,将其绘制到一个 ** 值。
在Go中, map 数据类型是大多数程序员认为的 dictionary 类型,它将密钥绘制为值,使密钥值对成为在Go中存储数据的一种有用的方法。
1map[key]value{}
在 Go 中,您通常会使用地图来存储相关数据,例如ID中包含的信息。
1map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
除了弯曲的键盘外,整个地图上还有连接关键值对的列。列左侧的单词是键盘。在Go中,键盘可以是任何类型的 comparable,如字符串
,注
等等。
示例地图中的密钥是:
*「名稱」 *「動物」 *「顏色」 *「位置」
列右侧的单词是值,值可以是任何数据类型,示例地图中的值是:
"Sammy"
"shark"
"blue"
"ocean"
与其他数据类型一样,您可以将地图存储在变量中,然后打印出来:
1sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
2fmt.Println(sammy)
这将是你的产量:
1[secondary_label Output]
2map[animal:shark color:blue location:ocean name:Sammy]
关键值对的顺序可能发生变化. 在 Go 中,地图数据类型不受排序. 无论顺序如何,关键值对将保持不变,允许您根据它们的关系意义访问数据。
访问地图项目
您可以通过引用相关密钥来调用地图的值,因为地图提供了存储数据的关键值对,因此它们可以在您的 Go 程序中成为重要的和有用的项目。
如果你想分离Sammy的用户名,你可以这样做,通过呼叫sammy
(名称
);持有你的地图和相关密钥的变量。
1fmt.Println(sammy["name"])
然后将值作为输出:
1[secondary_label Output]
2Sammy
地图就像数据库一样行事;而不是调用整数以获得特定索引值,就像你用片段一样,你将一个值分配给一个密钥,并调用该密钥以获得相关值。
通过召唤名称
密钥,您将获得该密钥的值,即Sammy
。
同样,您可以使用相同的格式调用sammy
地图中的剩余值:
1fmt.Println(sammy["animal"])
2// returns shark
3
4fmt.Println(sammy["color"])
5// returns blue
6
7fmt.Println(sammy["location"])
8// returns ocean
通过在地图数据类型中使用密钥值对,您可以引用密钥来检索值。
关键和价值观
与一些编程语言不同,Go 没有任何 convenience 函数来列出地图的密钥或值. 一个例子是 Python 的字典方法 .keys()
。
1for key, value := range sammy {
2 fmt.Printf("%q is the key for the value %q\n", key, value)
3}
在 Go 中通过地图时,它会返回两个值. 第一个值将是密钥,第二个值将是值. Go 将使用正确的数据类型创建这些变量. 在这种情况下,地图密钥是字符串
,所以字符串
也将是字符串。
1[secondary_label Output]
2"animal" is the key for the value "shark"
3"color" is the key for the value "blue"
4"location" is the key for the value "ocean"
5"name" is the key for the value "Sammy"
若要获取仅有密钥的列表,您可以再次使用范围操作员. 您可以声明只有一种变量才能访问只有这些密钥:
1keys := []string{}
2
3for key := range sammy {
4 keys = append(keys, key)
5}
6fmt.Printf("%q", keys)
该程序从声明一个片段来存储你的密钥开始。
输出只会显示您的地图的密钥:
1[secondary_label Output]
2["color" "location" "name" "animal"]
再次,密钥没有被排序,如果你想排序它们,你可以从 sort
包中使用 sort.Strings
函数:
1sort.Strings(keys)
使用此函数,您将收到以下输出:
1[secondary_label Output]
2["animal" "color" "location" "name"]
您可以使用相同的模式来检索地图中的值,在下一个示例中,您可以预先分配片段以避免分配,从而使程序更有效:
1sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
2
3items := make([]string, len(sammy))
4
5var i int
6
7for _, v := range sammy {
8 items[i] = v
9 i++
10}
11fmt.Printf("%q", items)
首先你声明一个片段存储你的密钥;因为你知道你需要多少个项目,你可以避免潜在的内存分配,通过定义该片段的相同大小。你然后声明你的索引变量。
1[secondary_label Output]
2["ocean" "Sammy" "shark" "blue"]
要确定地图中的项目数量,您可以使用内置的len
函数:
1sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
2fmt.Println(len(sammy))
输出显示您的地图中的项目数量:
1[secondary_label Output]
24
虽然Go不提供方便功能来获取密钥和值,但它只需要几行代码来获取密钥和值。
验证存在
由于这个原因,您需要一种替代的方式来区分存储的零,而不是缺少的密钥。
让我们在地图上查找你知道不存在的值,然后看看返回的值:
1counts := map[string]int{}
2fmt.Println(counts["sammy"])
您将看到以下输出:
1[secondary_label Output]
20
即使键sammy
不在地图中,Go 仍然返回值为0
,这是因为值数据类型是int
,并且因为Go 对所有变量都有零值,它返回的值为0
。
在许多情况下,这是不必要的,并会导致程序中的错误。当在地图中搜索值时,Go可以返回第二个,可选的值。第二个值是bool
,如果发现了密钥,它将是真
,或者如果没有找到密钥,它将是假
。在Go中,这被称为OK
语法。即使你可以命名捕捉第二个参数的变量,无论你想要什么,在Go中,你总是命名它为OK
:
1count, ok := counts["sammy"]
如果在计数
地图中存在sammy
键,那么ok
将是true
,否则ok
将是假的。
您可以使用OK
变量来决定在程序中要做什么:
1if ok {
2 fmt.Printf("Sammy has a count of %d\n", count)
3} else {
4 fmt.Println("Sammy was not found")
5}
这将导致以下产出:
1[secondary_label Output]
2Sammy was not found
在 Go 中,您可以将变量声明和条件检查与 if/else 块相结合,从而允许您为此检查使用单个声明:
1if count, ok := counts["sammy"]; ok {
2 fmt.Printf("Sammy has a count of %d\n", count)
3} else {
4 fmt.Println("Sammy was not found")
5}
在 Go 中从地图中检索一个值时,检查它的存在以及避免程序中的错误总是很好的做法。
地图变更
地图是一个可变的数据结构,所以您可以修改它们. 让我们看看本节中添加和删除地图项目。
添加和更改地图项目
您可以使用地图变量名称,然后是方块 [ ]
中的关键值,并使用等于 =
运算符设置新的值:
1map[key] = value
在实践中,您可以通过在名为用户名
的地图中添加一个关键值对来查看此工作:
1usernames := map[string]string{"Sammy": "sammy-shark", "Jamie": "mantisshrimp54"}
2
3usernames["Drew"] = "squidly"
4fmt.Println(usernames)
输出将在地图上显示新的Drew:squidly
键值对:
1[secondary_label Output]
2map[Drew:squidly Jamie:mantisshrimp54 Sammy:sammy-shark]
由于地图返回无序,这对可能发生在地图输出中的任何地方. 如果您在程序文件中稍后使用用户名
地图,它将包含额外的键值对。
您也可以使用此语法来更改分配给密钥的值,在这种情况下,您会参考现有密钥并传递另一个值。
考虑一个名为追随者
的地图,该地图跟踪特定网络上的用户的追随者。用户drive
今天遇到追随者的碰撞,所以你需要更新将整数值传给drive
键。
1followers := map[string]int{"drew": 305, "mary": 428, "cindy": 918}
2followers["drew"] = 342
3fmt.Println(followers)
您的输出将显示drive
的更新值:
1[secondary_label Output]
2map[cindy:918 drew:342 mary:428]
你可以看到,追随者的数量从305
的整数值跳到342
。
您可以使用此方法将关键值对添加到用户输入的地图中,让我们编写一个名为usernames.go
的快速程序,该程序运行在命令行上,并允许用户输入添加更多的名称和相关的用户名:
1[label usernames.go]
2package main
3
4import (
5 "fmt"
6 "strings"
7)
8
9func main() {
10 usernames := map[string]string{"Sammy": "sammy-shark", "Jamie": "mantisshrimp54"}
11
12 for {
13 fmt.Println("Enter a name:")
14
15 var name string
16 _, err := fmt.Scanln(&name)
17
18 if err != nil {
19 panic(err)
20 }
21
22 name = strings.TrimSpace(name)
23
24 if u, ok := usernames[name]; ok {
25 fmt.Printf("%q is the username of %q\n", u, name)
26 continue
27 }
28
29 fmt.Printf("I don't have %v's username, what is it?\n", name)
30
31 var username string
32 _, err = fmt.Scanln(&username)
33
34 if err != nil {
35 panic(err)
36 }
37
38 username = strings.TrimSpace(username)
39
40 usernames[name] = username
41
42 fmt.Println("Data updated.")
43 }
44}
在usernames.go
中,您首先定义原始地图,然后设置一个循环来重复名称,您要求您的用户输入一个名称,并声明一个变量以存储它,接下来,您检查是否有错误;如果是这样,程序将以 panic 离开。
如果
块检查名称是否存在于地图上,并打印反馈。如果名称存在,则继续返回循环的顶部。如果名称不在地图中,则向用户提供反馈,然后将要求新用户名为相关名称。该程序再次检查是否存在错误。没有错误,它删除车辆返回,将用户名值分配给名称密钥,然后打印了数据更新的反馈。
让我们在命令行上运行程序:
1go run usernames.go
您将看到以下输出:
1[secondary_label Output]
2Enter a name:
3Sammy
4"sammy-shark" is the username of "Sammy"
5Enter a name:
6Jesse
7I don't have Jesse's username, what is it?
8JOctopus
9Data updated.
10Enter a name:
当您完成测试时,请按CTRL + C
来逃离程序。
通过这个特定的程序,一旦你用CTRL + C
离开程序,你将失去所有的数据,除非你实施了一种方式来处理阅读和写入文件。
要总结,您可以将项目添加到地图或使用map[key] = value
语法修改值。
删除地图项目
就像您可以添加关键值对并在地图数据类型中更改值一样,您也可以在地图中删除项目。
要从地图中删除一个关键值对,可以使用内置函数 delete()
. 第一个参数是您正在删除的地图,第二个参数是您正在删除的密钥:
1delete(map, key)
让我们定义一个允许的地图:
1permissions := map[int]string{1: "read", 2: "write", 4: "delete", 8: "create", 16:"modify"}
您不再需要修改
权限,因此您将从地图中删除它,然后您将打印地图以确认已删除:
1permissions := map[int]string{1: "read", 2: "write", 4: "delete", 8: "create", 16: "modify"}
2delete(permissions, 16)
3fmt.Println(permissions)
输出将确认删除:
1[secondary_label Output]
2map[1:read 2:write 4:delete 8:create]
删除(允许, 16)
行从允许
地图中删除关键值对16:
修改``。
如果您想要清除所有值的地图,您可以通过将其设置为相同类型的空地图,从而创建一个新的空地图,并将旧地图从垃圾收集器的内存中清除。
让我们在允许
地图中删除所有项目:
1permissions = map[int]string{}
2fmt.Println(permissions)
输出显示,您现在有一个没有关键值对的空地图:
1[secondary_label Output]
2map[]
由于地图是可变的数据类型,可以添加、修改、删除和清除项目。
结论
本教程探讨了 Go 中的地图数据结构。地图由关键值对组成,并提供一种不依赖索引的数据存储方式,这使我们能够根据其含义和与其他数据类型的关系获取值。