许多语言中都会有的成对键值数据结构,在 Go 中是以内置类型map
来实现,格式为map[keyType]valueType
。
创建与初始化 map
想要创建例一个map
实例,但尚无任何键值对,可以使用make
函数,例如:
package main
import "fmt"
func main() {
passwords := make(map[string]int)
fmt.Println(passwords) // map[]
fmt.Println(len(passwords)) // 0
passwords["caterpillar"] = 123456
passwords["monica"] = 54321
fmt.Println(passwords) // map[caterpillar:123456 monica:54321]
fmt.Println(len(passwords)) // 2
fmt.Println(passwords["caterpillar"]) // 123456
fmt.Println(passwords["monica"]) // 54321
}
在上例中,passwords
是个参考,指向make(map[string]int)
创建的 map 实例。
类似一些语言(例如 Python),要设定一个键值对应时,使用[]
与=
指定,要获取键对应的值时,使用[]
指定键,这会返回对应的值,想知道map
中的键数,可以使用len
函数。
要注意的是,可用来做为键的值,必须是comparable,就目前来说,你要先知道的 comparable 类型有布尔、数字、字符串、指针(pointer)、channel、interface、struct,或者含有这这些类型的数组,这些是都可以使用==
来比较的值;而 slice、map 与函数,就不能用来做为键。
如果已知 map 中会有的键值对,则可以如下创建 map:
package main
import "fmt"
func main() {
passwords := map[string]int{
"caterpillar": 123456,
"monica": 54321,
}
fmt.Println(passwords) // map[monica:54321 caterpillar:123456]
fmt.Println(len(passwords)) // 2
fmt.Println(passwords["caterpillar"]) // 12345
fmt.Println(passwords["monica"]) // 54321
}
如果passwords
创建时,最后一个键值项目后不换行,那么最后一个逗号就不需要,例如:
passwords := map[string]int {"caterpillar" : 123456, "monica" : 54321}
实际上,你也可以写passwords := map[string]int {}
,来创建一个没有任何键值对的map
,这相当于写passwords := make(map[string]int)
,不过,若是var passwords map[string]int
的话,只是创建一个参考名称passwords
,默认空值是nil
,也就是相当于var passwords map[string]int = nil
的意思。
也就是说,var passwords map[string]int
定义了一个参考类型,两个map
类型的名称,可以指向同一个map
实例,透过其中一个名称来改变map
内容,从另一个名称就可以获得对应的修改:
package main
import "fmt"
func main() {
passwds1 := map[string]int{"caterpillar": 123456}
passwds2 := passwds1
fmt.Println(passwds1) // map[caterpillar:123456]
passwds2["monica"] = 54321
fmt.Println(passwds1) // map[monica:54321 caterpillar:123456]
}
键值访问、删除
如方才所看到的,要设定一个键值对应时,使用[]
与=
指定,要获取键对应的值时,使用[]
指定键,这会返回对应的值,如果指定的键不存在,那么会返回值类型对应的空值,例如,若passwords := map[string]int {"caterpillar" : 123456}
,那么passwords["monica"]
会返回 0。
不过,更精确地说,使用mapName[key]
时,会返回两个值(Go 中允许同时返回多值),第一个是键对应的值,若没有该键就返回值类型的空值,第二个是布尔值,指出键是否存在。例如:
package main
import "fmt"
func main() {
passwds := map[string]int{"caterpillar": 123456}
v, exists := passwds["monica"]
fmt.Printf("%d %t\n", v, exists) // 0 false
passwds["monica"] = 54321
v, exists = passwds["monica"]
fmt.Printf("%d %t\n", v, exists) // 54321 true
}
因此,若只是单纯想测试键是否存在,只要用下划线_
忽略返回的值就可以了,例如:
package main
import "fmt"
func main() {
passwds := map[string]int{"caterpillar": 123456}
name := "caterpillar"
_, exists := passwds[name]
if exists {
fmt.Printf("%s's password is %d\n", name, passwds[name])
} else {
fmt.Printf("No password for %s\n", name)
}
}
exists
的指定与if
的判断也可以写在同一行:
if _, exists := passwds[name]; exists {
fmt.Printf("%s's password is %d\n", name, passwds[name])
} else {
fmt.Printf("No password for %s\n", name)
}
如果想删除某个键值,可以使用delete
函数,例如delete(passwds, "caterpillar")
。
迭代键值
如果要迭代map
的键值,可以使用for range
,例如:
package main
import "fmt"
func main() {
passwords := map[string]int{
"caterpillar": 123456,
"monica": 54321,
}
for name, passwd := range passwords {
fmt.Printf("%s : %d\n", name, passwd)
}
}
如果只是想迭代map
的键,可以如下:
package main
import "fmt"
func main() {
passwords := map[string]int{
"caterpillar": 123456,
"monica": 54321,
}
for name := range passwords {
fmt.Printf("%s\n", name)
}
}
如果只想迭代map
的值,可以如下:
package main
import "fmt"
func main() {
passwords := map[string]int{
"caterpillar": 123456,
"monica": 54321,
}
for _, passwd := range passwords {
fmt.Printf("%d\n", passwd)
}
}
如果想获取map
中的键清单或者是值清单,方式之一是使用 slice 进行收集,例如:
package main
import "fmt"
func keys(m map[string]int) []string {
ks := make([]string, 0, len(m))
for k := range m {
ks = append(ks, k)
}
return ks
}
func values(m map[string]int) []int {
vs := make([]int, 0, len(m))
for _, v := range m {
vs = append(vs, v)
}
return vs
}
func main() {
passwords := map[string]int{
"caterpillar": 123456,
"monica": 54321,
}
fmt.Println(keys(passwords)) // [caterpillar monica]
fmt.Println(values(passwords)) // [123456 54321]
}
Go 的map
在迭代时没有一定的顺序,如果想要有排序结果,必须自行处理,例如,针对键排序来进行迭代:
package main
import "sort"
import "fmt"
func main() {
passwords := map[string]int{
"caterpillar": 123456,
"monica": 54321,
"hamimi": 13579,
}
keys := make([]string, 0, len(passwords))
for key := range passwords {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
fmt.Printf("%s : %d\n", key, passwords[key])
}
}