第十三篇 映射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

}

在上面程序中,employeeSalarynil,我们给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结构体包含了两个字段salarycontry,我们创建了三个员工emp1,emp2emp3

然后就初始化了映射内容为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 := employeeSalaryemployeeSalary赋值给modified,下一行,修改了映射modifiedlisi的工资为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,我鼓励你用这个写一个新的程序。

以上就是本篇的全部,感谢阅读,这是我第一次翻译,难免会有翻译不当的地方,如果有什么反馈和评论,欢迎提出来!

原文地址: https://golangbot.com/maps/