Go基础-006-03 复合数据类型 映射表

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"]

你可能感兴趣的:(Go基础-006-03 复合数据类型 映射表)