[译] part 13: golang 映射 map

https://juejin.im/post/5cb35e5de51d456e811d2688

什么是 map

map 是 Go 中的内置类型,它将值与键相关联。可以使用相应的键查找该值。

怎么创建 map

可以指定键和值的类型,然后通过make函数来创建一个 map,语法为make(map[type of key]type of value)

  1. personSalary := make(map[string]int)

上面的代码创建了一个叫personSalary的 map,它的键的类型为string,值的类型为int

map 的零值为nil,如果你尝试给空 map 增加元素,运行时将会触发 panic,因此,必须使用make函数初始化 map。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. var personSalary map[string]int
  7. if personSalary == nil {
  8. fmt.Println("map is nil. Going to make one.")
  9. personSalary = make(map[string]int)
  10. }
  11. }

Run in playground

在上述代码中,personSalary是空的,所以必须用 make 函数初始化。该代码输出map is nil. Going to make one.

给 map 增加元素

给 map 增加元素的语法和数组一样,下面的代码将给personSalary增加新的元素。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := make(map[string]int)
  7. personSalary["steve"] = 12000
  8. personSalary["jamie"] = 15000
  9. personSalary["mike"] = 9000
  10. fmt.Println("personSalary map contents:", personSalary)
  11. }

Run in playground

上面的程序将会输出,personSalary map contents: map[steve:12000 jamie:15000 mike:9000]

map 也可以在初始化的时候添加元素,

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := map[string]int {
  7. "steve": 12000,
  8. "jamie": 15000,
  9. }
  10. personSalary["mike"] = 9000
  11. fmt.Println("personSalary map contents:", personSalary)
  12. }

Run in playground

上面的程序声明了personSalary,并在声明过程中为它添加了两个元素。之后又添加了一个键为mike的元素。该程序输出

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

键的类型并不仅仅是string。所有可比较的类型,如booleanintegerfloatcomplexstring,…也可以是键。如果你想了解有关类似类型的更多信息,请访问http://golang.org/ref/spec#Comparison_operators

访问 map 的元素

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

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := map[string]int{
  7. "steve": 12000,
  8. "jamie": 15000,
  9. }
  10. personSalary["mike"] = 9000
  11. employee := "jamie"
  12. fmt.Println("Salary of", employee, "is", personSalary[employee])
  13. }

Run in playground

上述程序非常简单。员工 Jamie 的工资被找到并打印出来。程序输出Salary of jamie is 15000.

如果元素不存在会发生什么?map 将返回该元素类型的零值。在personSalary的例子中,如果我们尝试访问当前不存在的元素,则返回int的零值 0。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := map[string]int{
  7. "steve": 12000,
  8. "jamie": 15000,
  9. }
  10. personSalary["mike"] = 9000
  11. employee := "jamie"
  12. fmt.Println("Salary of", employee, "is", personSalary[employee])
  13. fmt.Println("Salary of joe is", personSalary["joe"])
  14. }

Run in playground

上面的程序将输出,

  1. Salary of jamie is 15000
  2. Salary of joe is 0

上面的程序返回了 joe 的工资为 0,我们并没有得到 joe 不存在于personSalary中的信息。

如果我们想知道 map 中是否存在某个键,该怎么办?

  1. value, ok := map[key]

上面的语法就是找出 map 中是否存在某个键,如果oktrue的话,那就说明键存在,并且值为value。否则okfalsevalue为空。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := map[string]int{
  7. "steve": 12000,
  8. "jamie": 15000,
  9. }
  10. personSalary["mike"] = 9000
  11. newEmp := "joe"
  12. value, ok := personSalary[newEmp]
  13. if ok == true {
  14. fmt.Println("Salary of", newEmp, "is", value)
  15. } else {
  16. fmt.Println(newEmp,"not found")
  17. }
  18. }

Run in playground

上述程序的第 15 行,由于 joe 不存在,okfalse。因此程序输出

  1. joe not found

使用range for可以迭代所有的 map 元素。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := map[string]int{
  7. "steve": 12000,
  8. "jamie": 15000,
  9. }
  10. personSalary["mike"] = 9000
  11. fmt.Println("All items of a map")
  12. for key, value := range personSalary {
  13. fmt.Printf("personSalary[%s] = %d\n", key, value)
  14. }
  15. }

Run in playground

上述程序输出,

  1. All items of a map
  2. personSalary[mike] = 9000
  3. personSalary[steve] = 12000
  4. personSalary[jamie] = 15000

一个重要的事实是,使用range for迭代获取元素的顺序,并不能保证结果每次相同。

删除元素

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

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := map[string]int{
  7. "steve": 12000,
  8. "jamie": 15000,
  9. }
  10. personSalary["mike"] = 9000
  11. fmt.Println("map before deletion", personSalary)
  12. delete(personSalary, "steve")
  13. fmt.Println("map after deletion", personSalary)
  14. }

Run in playground

上述程序删除 key 为 steve 的元素,程序输出

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

map 的长度

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

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := map[string]int{
  7. "steve": 12000,
  8. "jamie": 15000,
  9. }
  10. personSalary["mike"] = 9000
  11. fmt.Println("length is", len(personSalary))
  12. }

Run in playground

上述程序使用len(personSalary)计算 map 的长度,输出,length is 3

map 是指针传递

和 slice 一样,map 也是指针传递的。将 map 赋值给新变量时,它们都指向相同的内部数据结构。因此修改一个会影响另外一个。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. personSalary := map[string]int{
  7. "steve": 12000,
  8. "jamie": 15000,
  9. }
  10. personSalary["mike"] = 9000
  11. fmt.Println("Original person salary", personSalary)
  12. newPersonSalary := personSalary
  13. newPersonSalary["mike"] = 18000
  14. fmt.Println("Person salary changed", personSalary)
  15. }

Run in playground

在上述代码的第 14 行,personSalary赋值给newPersonSalary。在下一行,mike 的工资被修改为 18000,personSalary中的薪水也会变成 18000。程序输出,

  1. Original person salary map[steve:12000 jamie:15000 mike:9000]
  2. Person salary changed map[steve:12000 jamie:15000 mike:18000]

类似的,如果将 map 作为函数的接收者。当对函数内的 map 进行更改时,调用者将可以看到更改。

map 的等值比较

map 的等值比较不能使用==操作符,==操作符仅仅能用来检查该 map 是否为nil

  1. package main
  2. func main() {
  3. map1 := map[string]int{
  4. "one": 1,
  5. "two": 2,
  6. }
  7. map2 := map1
  8. if map1 == map2 {
  9. }
  10. }

Run in playground

上述的代码将会抛出编译错误,invalid operation: map1 == map2 (map can only be compared to nil).

检查两个 map 是否相等的方法是逐个比较每个元素是否一样。为此编写一个函数是个不错的方法:)

ft_authoradmin  ft_create_time2019-08-03 16:34
 ft_update_time2019-08-03 16:36