Go 的 map

134 阅读4分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。

概念

  • 中文叫映射,感觉就是 Python 的 dict 字典,是一种无序的键值对的集合
  • 映射是方便而强大的内建数据结构,它可以关联不同类型的值

map 的特点

  • map 是无序的,每次打印出来的 map 都会不一样,它不能通过 index 获取,而必须通过 key 获取
  • map 的长度是不固定的,这是因为 map 是使用 hash 表来实现的
  • map 和 slice 一样,是一种引用类型,也可以像迭代数组和切片那样迭代它
  • 内置的 len 函数同样适用于 map,返回 map 拥有的 key 的数量
  • map 的 key 可以是所有可比较的类型,整数、浮点数、复数、字符串、指针、接口(只要其动态类型支持相等性判断)、结构以及数组,但切片不能用作映射键,因为它们的相等性还未定义

创建 map

package main

import "fmt"

func main() {
    // 有初始数据
	var timeZone = map[string]int{
		"UTC": 0 * 60 * 60,
		"EST": -5 * 60 * 60,
		"CST": -6 * 60 * 60,
		"MST": -7 * 60 * 60,
		"PST": -8 * 60 * 60,
	}

	intMap := map[int]int{
		1: 123,
		2: 112233,
		3: 111222333,
	}
	fmt.Println(timeZone, intMap)
}

运行结果

map[CST:-21600 EST:-18000 MST:-25200 PST:-28800 UTC:0] map[1:123 2:112233 3:111222333]

map 的默认值是 nil

nil 映射既没有键,也不能添加键值对,如果添加会报错,需要通过 make 来添加

package main

import "fmt"

func main() {
	var strMap map[string]string
	fmt.Printf("%T, %v, %v \n", strMap, strMap, len(strMap))
	
    // 下面代码会报错
	strMap["name"] = "小菠萝测试"
	strMap["age"] = "24"
}

运行结果

map[string]string, map[], 0 
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
	/tmp/sandbox3315044160/prog.go:9 +0x9f

Program exited.

使用 make 创建

package main

import "fmt"

func main() {
	// 一行代码创建一个空 map
	strMap := make(map[string]string)
	strMap["name"] = "小菠萝测试"
	strMap["age"] = "24"

	fmt.Println( strMap, strMap["name"])
}

运行结果

map[age:24 name:小菠萝测试] 小菠萝测试

for 循环读取 map

用 range 形式去循环读取 map 的时候,第一个值是 key,第二个值是 value

package main

import "fmt"

func main() {
	// 一行代码创建
	var timeZone = map[string]int{
		"UTC": 0 * 60 * 60,
		"EST": -5 * 60 * 60,
		"CST": -6 * 60 * 60,
		"MST": -7 * 60 * 60,
		"PST": -8 * 60 * 60,
	}

	for key := range timeZone {
		fmt.Println(timeZone[key])
	}
}

运行结果

0
-18000
-21600
-25200
-28800

双赋值检测键是否存在

  • 读取 map 的 key 时会返回两个值
  • 第一个值就是 key 对应的 value
  • 第二个值是布尔值,如果 key 存在则 true,不存在则 false,可以根据这个值来做条件判断
  • 如果 key 不存在,则会返回 value 对应类型的零值,
package main

import "fmt"

func main() {
	// 一行代码创建
	var timeZone = map[string]int{
		"UTC": 0 * 60 * 60,
		"EST": -5 * 60 * 60,
		"CST": -6 * 60 * 60,
		"MST": -7 * 60 * 60,
		"PST": -8 * 60 * 60,
	}

	value, ok := timeZone["CST"]
	fmt.Printf("%v, %v \n", value, ok)

	value, ok = timeZone["NO"]
	fmt.Printf("%v, %v", value, ok)

}

运行结果

-21600, true
0, false

条件判断

跟着上面的🌰 ,将读取 map[key] 封装成一个函数

package main

import (
	"fmt"
	"log"
)

func main() {

	// 条件判断
	var timeZone = map[string]int{
		"UTC": 0 * 60 * 60,
		"EST": -5 * 60 * 60,
		"CST": -6 * 60 * 60,
		"MST": -7 * 60 * 60,
		"PST": -8 * 60 * 60,
	}
	fmt.Println(offset(timeZone, "CST"))
	offset(timeZone, "NO")
}

func offset(myMap map[string]int, key string) int {
	if value, ok := myMap[key]; ok {
		return value
	}
	log.Println("unknown time zone:", key)
	return 0
}

运行结果

-21600
2009/11/10 23:00:00 unknown time zone: NO

map 是引用类型,指针传递

  • 前面说了,map 和切片类似,都是引用类型
  • 当不同变量指向同一个 map 的时候,通过不同变量修改 map 的值,会影响其他变量

🌰 一:多个变量指向同一个 map

package main

import "fmt"

func main() {
	map1 := map[string]string{
		"France": "1",
		"Italy":  "2",
		"Japan":  "3",
		"India":  "4",
	}

	map2 := map1
	fmt.Println(map1, map2)

	map2["China"] = "5"
	delete(map1, "India")

	fmt.Println(map1, map2)
}

运行结果

map[France:1 India:4 Italy:2 Japan:3] map[France:1 India:4 Italy:2 Japan:3]
map[China:5 France:1 Italy:2 Japan:3] map[China:5 France:1 Italy:2 Japan:3]

🌰 二:map 作为函数的参数

package main

import "fmt"

func main() {
	myMap := map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
	}
	fmt.Println(myMap)

	addMap(myMap)
	fmt.Println(myMap)
}

func addMap(myMap map[string]int) {
	myMap["func"] = 2
	myMap["ok"] = 1
}

运行结果

map[a:1 b:2 c:3]
map[a:1 b:2 c:3 func:2 ok:1]

map 不能使用 == 操作符进行比较

==只能用来检查map是否为空

package main

import "fmt"

func main() {
	map1 := map[string]string{
		"France": "1",
		"Italy":  "2",
	}

	map2 := map1

	fmt.Println(map1 == map2)
}

运行结果

./prog.go:15:19: invalid operation: map1 == map2 (map can only be compared to nil)