1.类型定义
映射表 也叫,键值对集合, key=>value 对集合。
不同于数组和切片(索引值对集合)下表仅仅是由 0 开始逐一递增的整型,map 的下标(键)可以是任意的可比较类型(目前为止,只有切片是不可比较类型),最常用的还是整型和字符串型。
映射表还被称为 关联数组,哈希表。
定义语法:map[keyT]valueT
代码示例:
func main() {
var userSet map[uint]string
fmt.Printf("%T\n", userSet) // map[uint]string
fmt.Println(userSet) // map[]
}
不限定元素的数量,操作上也没有容量的概念。仅仅需要依据 key 和 value 的类型,进 行元素操作即可。
代码演示:
func main() {
userSet := map[uint]string{}
userSet[12] = "张大锤"
userSet[22] = "李二锤"
fmt.Println(userSet) // map[12:张大锤 22:李二锤]
}
注意:
- 与 slice 一致,map 类型也是引用类型。
- 仅仅 var 声明但未指定初始值的话,使用 nil 作为零值,不能直接继续后续的操作,还是建议使用 make()或者是字面量的方式进行声明。
- 与切片和数组的差异:
切片和数组的下标,永远是从 0 开始逐一递增的整数,表示的元素的位置。
映射表,下标 key,不表示元素位置,仅仅是与值对应关系。不能使用下标去衡量元素 的位置和顺序。(map 元素的是无序的)
2.键的数据类型是可以等值比较(==)的任意数据类型
演示,布尔型和数组型都可以:
m2 := map[bool]int{
false: 1,
true: 1000,
}
fmt.Println(m2)
m3 := map[[2]int]int {
[2]int{1, 2}: 42,
[2]int{3, 4}: 1024,
}
fmt.Println(m3)
3.字面量
字面量语法如下:
map[keyT]valueT{}
map[keyT]valueT{
key1: value1,
key2: value2,
}
示例:
userSet := map[uint]string{}
m2 := map[bool]int{
false: 1,
true: 1000,
}
4.引用类型
map 型数据结构,也包含数据值的地址,也是引用类型。
演示:
m6 := map[string]int{
"hank": 42,
"blockchain": 1024,
}
m7 := m6
m7["hank"] = 365
fmt.Println(m6, m7)
// 输出:map[blockchain:1024 hank:365] map[blockchain:1024 hank:365]
5.map的元素是无序的
- 存储的顺序,与语法的顺序不能保证一致。
- 遍历获取元素是顺序,不能保证一致:一指的是不能保证与语法顺序一致; 二指的是不能保证每次遍历的顺序一致。
遍历的语法仍然是 for range
6.操作
1)[] key 操作
通过[] 利用 key,访问到特定的元素。
代码:
m4 := map[string]int{
"hank": 42,
"fire": 1024,
"links": 365,
}
fmt.Println(m4["hank"]) // 42
key := "links"
fmt.Println(m4[key]) // 365
fmt.Println(m4["f"+"i"+"re"]) // 1024
2)len(),元素个数
m1 := map[string]int{
"hank": 42,
"fire": 1024,
"links": 365,
}
fmt.Println(len(m1)) // 3
3)存在判断
当使用[]key 的方案,访问元素时,若指定的元素不存在,则会返回元素类型的零值。
代码演示:
m4 := map[string]int{
"hank": 42,
"fire": 1024,
"links": 365,
}
fmt.Println(m4["chains"]) // 0
m5 := map[string]bool{
"yes": true,
}
fmt.Println(m5["no"]) // false
可见,访问不存在的元素,不会导致失败,反而会常规返回内容。因此不能区别,是否存在 该元素。
若需要明确,该元素是否存在,则需要使用判定语法,如下:
value, exists := mapData[key]
在获取值时,利用多值返回的方案来获取,返回的第二个值,是布尔值,用于表示是否获取 成功(是否存在该元素),演示:
m4 := map[string]int{
"hank": 42,
"fire": 1024,
"links": 365
}
v1, exists := m4["hank"]
fmt.Println(v1, exists) // 42 true
v2, e := m4["chains"]
fmt.Println(v2, e) // 0 false
因此,在需要判断的场合,先判断 e 的值,再处理元素值,例如:
m4 := map[string]int{
"hank": 42,
"fire": 1024,
"links": 365
}
v3, e := m4["blockchain"]
if !e { // 不存在
v3 = 666 // 默认值
}
fmt.Println(v3) // 666
4)delete(), 删除元素
map 的元素可以被删除。
使用内置函数 delete()实现,语法: delete(map, key1, key2...)
代码演示:
m4 := map[string]int{
"hank": 42,
"fire": 1024,
"links": 365,
}
fmt.Println(m4["fire"]) //1024
delete(m4, "fire")
fmt.Println(m4, m4["fire"]) // map[hank:42 links:365] 0
5)nil比较
nil 是 map 的零值,当仅仅使用 var 声明类型时,map 为 nil。
代码演示:
var m7 map[string]int
fmt.Println(m7, m7 == nil) // map[] true
6.for range 遍历
语法上和数组及切片一致:
支持,同时获取 key 和 value,仅仅获 key,和仅获取 value ,语法如下:
for k, v := range m7 {
fmt.Println(k, v)
}
for k := range m7 {
fmt.Println(k)
}
for _, v := range m7 {
fmt.Println(v)
}
示例:
m7 := map[string]int{
"hank": 42,
"blockchain": 1024,
"firelinks": 365,
}
for k, v := range m7 {
fmt.Println(k, v)
}
for k := range m7 {
fmt.Println(k)
}
for _, v := range m7 {
fmt.Println(v)
}
注意:
遍历获取元素是顺序,不能保证一致:一指的是不能保证与语法顺序一致,二指的是不能保证每次遍历的顺序一致。
7.Set性,集合性
在 GO 经常将 map 类型,作为 set 集合类型看待。set 类型具备典型特征,map 都具
备:
- 元素不能重复
- 元素无序
体现在 map 上,通过 key 进行体现的。
在 map 中,key 是不能重复而且是无序的。
代码:
set := map[string]int{
"hank": 42,
"firelinks": 365,
"hank": 1024,
}
fmt.Println(set) // 报错: # command-line-arguments .\map.go:98:3: duplicate key "hank" in map literal
因此在程序处理时,若需要使用 set 集合,map 的元素值,意义不大,通常为一个最简 单的数据结构即可,代码演示如下:
//典型的集合
set := map[string]int8{
"hank": 1,
"fire": 1,
"links": 1,
"blockchain": 1,
}
for k := range set {
fmt.Println(k)
}
_, e := set["hank"]
if e {
} else {
}
8.映射表元素不可用& 来取地址
映射表又称 关联数组(哈希表),建立键和值的关系,通常叫做哈希函数,也叫映射函数。
在 map 中,维护的函数,函数的参数就是 key,函数的返回值就是 value,即:
value(valueAddress) = mapFunc(key)
由于是算出来的,可见元素是动态的,不能完全确定其位置。所以产生语法现象为: 映射表的元素,不能直接使用 & 来去地址。(但是 array,slice 就可以直接使用&对元素取地址)。
代码演示:
//数组元素取地址, ok
arr := [...]int{1,2,3,4}
ep := &arr[2]
*ep = 33
fmt.Println(arr, ep)
//map 的元素取地址,失败
m := map[string]int{
"hank":42,
"fire": 1024,
}
p := &m["hank"] // 报错:cannot take the address of m["hank"]