第十三篇 映射Maps(键值对)
欢迎来到Golang教程系列的第十三篇
什么是映射
映射(map)是Go的内置类型,使用的是key-value键值对。让我们以一个有几个员工的创业公司为例来说明。为简单起见,我们假设员工的名字都是唯一的,然后我们准备一个数据结构来保存员工的工资。映射能完美适配这个用例,可以用员工的name作为它的key,而工资作为value。映射(maps)有点像其它语言中的字典,例如pathon。
如何创建映射(map)
可以通过传递key和value的type给make函数来创建map.下面是创建一个新映射的语法。
make(map[type of key]type of value)
employeeSalary := make(map[string]int)
上面的代码创建了一个映射employeeSalary,它有一个string类型的key和int类型的value。
package main
import "fmt"
func main() {
employeeSalary := make(map[string]int)
fmt.Println(employeeSalary)
}
上面的程序创建了一个带有string类型的key和int类型value的映射employeeSalary。上面的程序会输出。
map[]
因为我们没有给映射添加元素,所以它是empty的。
给映射添加条目
给映射添加条目的方式和数组array的一样,下面是给映射employeeSalaty新增一些员工。
package main
import "fmt"
func main() {
employeeSalary := make(map[string]int)
employeeSalary["zhangsan"] = 11000
employeeSalary["lisi"] = 12000
employeeSalary["wangwu"] = 13000
fmt.Println("employeeSalary maps content",employeeSalary)
}
我们添加了zhangsan,lisi,wangwu三个员工和他们相应的工资。
上面的程序会打印。
employeeSalary maps content map[lisi:12000 wangwu:13000 zhangsan:11000]
还有一种方式是在映射声明语句的时候初始化。
package main
import "fmt"
func main() {
employeeSalary := map[string]int {
"zhangsan":11000,
"lisi":12000,
}
employeeSalary["wangwu"] = 13000
fmt.Println("employeeSalary maps content",employeeSalary)
}
上面的程序在声明employeeSalary的时候新增了两个元素,后面又新增了一个元素。这个程序打印:
employeeSalary maps content map[lisi:12000 wangwu:13000 zhangsan:11000]
不是只有string类型才可以作为key的,所有同类的类型例如布尔,整数,浮点数,复合数字符串等等都可以作为key。甚至用户自定义的类型,例如struct都可以作为keys。如果你想知道更多有关的类型,可以访问了解:http://golang.org/ref/spec#Comparison_operators
映射的零值
映射的零值是nil,如果你给一个nil的映射添加元素,就会出现run-time panic(运行恐慌?),因此,映射必须先初始化,而后才可以添加元素。
package main
func main() {
var employeeSalary map[string]int
employeeSalary["wangwu"] = 13000
}
在上面程序中,employeeSalary是nil,我们给map新增一个key,程序会出现恐慌和报错panic: assignment to entry in nil map
在映射中根据key检索value
我们现在已经给map添加了一些元素,我们将学习如何检索她们。map[key]就是检索映射中元素的语法。
package main
import "fmt"
func main() {
employeeSalary := map[string]int {
"zhangsan":11000,
"lisi":12000,
"wangwu":13000,
}
employee := "lisi"
salary := employeeSalary[employee]
fmt.Println("salary of",employee, "is", salary)
}
上面的程序相当简单,检索员工lisi的工资,并且打印出来。
salary of lisi is 12000
如果元素不存在的话会怎么样?那么映射就会返回元素类型的零值。在例子中的映射employeeSalary,我们试着获取一个不存在的元素,那么它就会返回int的零值0.
package main
import "fmt"
func main() {
employeeSalary := map[string]int {
"zhangsan":11000,
"lisi":12000,
"wangwu":13000,
}
fmt.Println("salary of zhaoliu is", employeeSalary["zhaoliu"])
}
上面程序输出:
salary of zhaoliu is 0
上面程序中返回zhaoliu的工资为0,当我们获取映射中不存在的key时,是不会报错的。
检查key是否存在
在上面小节中,我们学习了当一个key不存在时,就会返回零值。但是这并不能帮助我们找出在映射map中key是否存在。
例如,我们想知道在employeeSalary中是否存在某个key.
value, ok := map[ley]
上面的语法就是找出在一个映射中是否存在特定的值。如果ok是true,那么key就是存在的,它的value会出现变量value中,否则的话,key不存在。
package main
import "fmt"
func main() {
employeeSalary := map[string]int {
"zhangsan":11000,
"lisi":12000,
}
newEmp := "wangwu"
value, ok := employeeSalary[newEmp]
if ok == true {
fmt.Println("salary of",newEmp, "is", value)
return
}
fmt.Println(newEmp, "not found")
}
在上面程序中,ok为false,因此wangwu不存在,程序将会输出
wangwu not found
迭代映射中的元素
range是以for循环的形式用来迭代一个映射的所有元素。
package main
import "fmt"
func main() {
employeeSalary := map[string]int {
"zhangsan":11000,
"lisi":12000,
"wangwu":13000,
}
fmt.Println("Content of map")
for key, value := range employeeSalary {
fmt.Printf("employeeSalary[%s] = %d\n", key, value)
}
}
上面的程序输出
Content of map
employeeSalary[zhangsan] = 11000
employeeSalary[lisi] = 12000
employeeSalary[wangwu] = 13000
一个重要的事实是,使用for range获取映射中所有的值的顺序并不保证每次都一样的,新增元素到映射中获取的顺序也将会不一样,就是说每次获取值的顺序是可变的。
删除映射中的条目
delete(map, key)是删除一个map中的key的语法,delete函数不会返回任何值。
package main
import "fmt"
func main() {
employeeSalary := map[string]int {
"zhangsan":11000,
"lisi":12000,
"wangwu":13000,
}
fmt.Println("map before deletion", employeeSalary)
delete(employeeSalary, "wangwu")
fmt.Println("map after deletion", employeeSalary)
}
上面的程序删除了wangwu,并打印出
map before deletion map[lisi:12000 wangwu:13000 zhangsan:11000]
map after deletion map[lisi:12000 zhangsan:11000]
如果我们要删除一个映射中不存在的key,运行时也并不会报错。
映射的结构体struct
到目前为止,我们只学习了在map中存储员工的工资。如果我们还要在map中保存每个员工的国籍,那要怎么做呢?我们可以通过使用映射结构体来实现。员工可以用一个包含工资和国籍的结构体来代替,通过在map中保存string key 和 struct value,下面实践出真知。
package main
import "fmt"
type employee struct {
salary int
contry string
}
func main() {
emp1 := employee{
salary: 11000,
contry: "USA",
}
emp2 := employee{
salary: 12000,
contry: "Canada",
}
emp3 := employee{
salary: 13000,
contry: "China",
}
employeeInfo := map[string]employee{
"zhangsan": emp1,
"lisi": emp2,
"wangwu": emp3,
}
for name, info := range employeeInfo {
fmt.Printf("employee: %s salary: %d, contry: %s\n", name, info.salary, info.contry)
}
}
在上面程序中,employee结构体包含了两个字段salary和contry,我们创建了三个员工emp1,emp2和emp3。
然后就初始化了映射内容为string类型的key和我们刚创建的employee类型的value。
然后我们迭代了映射,并且在每一行打印员工的详情,程序输出如下:
employee: zhangsan salary: 11000, contry: USA
employee: lisi salary: 12000, contry: Canada
employee: wangwu salary: 13000, contry: China
映射的长度
映射的长度是可以通过使用len函数来获取。
package main
import "fmt"
func main() {
employeeInfo := map[string]int{
"zhangsan": 11000,
"lisi": 12000,
}
fmt.Println("length is", len(employeeInfo))
}
len(employeeSalary)在上面程序中返回了map的长度,下面是输出
length is 2
Maps是引用类型
跟切片(slices)相似,maps也是引用类型。当把一个map赋值给一个新的变量时,它们都指向同一个内部数据结构。因此,改变其中一个也会反射到另外一个。
package main
import "fmt"
func main() {
employeeSalary := map[string]int{
"zhangsan": 11000,
"lisi": 12000,
"wangwu": 13000,
}
fmt.Println("Original employee salary", employeeSalary)
modified := employeeSalary
modified["lisi"] = 18000
fmt.Println("Employee salary changed", employeeSalary)
}
在上面程序中,modified := employeeSalary, employeeSalary赋值给modified,下一行,修改了映射modified的lisi的工资为18000,在employeeSalary中lisi的工资也会为18000。上面程序输出:
Original employee salary map[lisi:12000 wangwu:13000 zhangsan:11000]
Employee salary changed map[lisi:18000 wangwu:13000 zhangsan:11000]
maps相等
map不可以用==操作符来比较,==只可以用来检查map是否为nil。
package main
func main() {
map1 := map[string]int{
"one":1,
"two":2,
}
map2 := map1
if map1 == map2 {
}
}
上面的程序将会编译错误。
invalid operation: map1 == map2 (map can only be compared to nil)
有一个检查两个map是否相等的方法是一个个地比较每一个元素。另外一种方法是使用reflection,我鼓励你用这个写一个新的程序。
以上就是本篇的全部,感谢阅读,这是我第一次翻译,难免会有翻译不当的地方,如果有什么反馈和评论,欢迎提出来!
浙公网安备 33010602011771号