Map的使用-Golang-100-Days/day08_Map%E7%9A%84%E4%BD%BF%E7%94%A8.md)

1.1 什么是Map

Map是Go中文内置类型,它将一个值与一个键关联起来。可以使用相应的键检索值。
Map是一种无需的键值对的集合。Map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值Map是一种集合,所以可以像迭代数组和切片那样迭代它,
Map是无序的,我们无法决定它的返回顺序,这是因为Map是使用hash表来实现的,也是引用类型。
使用map过程中需要注意的几点:

  1. map是无序的,每次打印出来的map都会不一样,它不能通过获取index获取,而必须通过key获取
  2. map和slice一样,也是一种引用类型
  3. 内置的len函数同样适用于map,返回map拥有的key的数量
  4. map的key可以是所有可比较的类型,如布尔型、整数型、浮点型、复杂型、字符串型…..所以数组、切片、结构体不能作为key。
  5. map的值可以是任意类型,可以通过使用空接口类型({}interfaces),可以存储任何值,包括函数(可以用来做分支结构,key用来选择要执行的函数)

    含有数组切片的结构体不能作为key,只包含内建类型的struct是可以作为key的),但是指针和接口类型可以。如果咬用结构体作为key,可以提供key()和Hash()方法,这样可以通过结构体的域计算出唯一的数组或者字符串的key。

1.2 Map的使用

1.1.1 创建map

map是引用类型,使用下面的结构进行声明

  1. var map1 map[keytye]valuetype
  2. 示例:
  3. var map1 map[string]int

具体示例:

  1. // 初始化方法一,默认值是nil
  2. var map1 map[string]int
  3. map1 = map[string]int{"China": 1, "France": 2}
  4. fmt.Println("map1:", map1)
  5. var map2 = make(map[string]int)
  6. map2["China"] = 1
  7. map2["France"] = 2
  8. fmt.Println("map2:", map2)
  9. // 初始化方法二,默认值是nil
  10. rating := map[string]float32{"C": 5, "GO": 4.5, "Python": 4.5, "C++": 2}
  11. fmt.Println(rating)
  12. // 初始化方法三
  13. countryCapitalMap := make(map[string]string)
  14. countryCapitalMap["France"] = "Paris"
  15. countryCapitalMap["Italy"] = "Rome"
  16. countryCapitalMap["Japan"] = "Tokyo"
  17. countryCapitalMap["India"] = "new Delhi"
  18. fmt.Println(countryCapitalMap)
  19. /*
  20. map1: map[China:1 France:2]
  21. map2: map[China:1 France:2]
  22. map[C:5 C++:2 GO:4.5 Python:4.5]
  23. map[France:Paris India:new Delhi Italy:Rome Japan:Tokyo]
  24. */

永远用make来构造map,不能使用new,如果错误的使用new()分配了一个对象,会获得一个空引用的指针,相当于声明了一个未初始化的变量,并且取了它的地址。

1.1.1.1 特例-使用函数作为值的map

  1. func main() {
  2. userFuncAsArg()
  3. }
  4. func userFuncAsArg() {
  5. m := make(map[int]func())
  6. m[1] = f1
  7. m[2] = f2
  8. m[3] = f3
  9. fmt.Println(m)
  10. // 调用函数
  11. for i, v := range m {
  12. fmt.Print("索引是[", i, "] 内容是:")
  13. v()
  14. }
  15. }
  16. func f1() {
  17. fmt.Println("这是f1")
  18. }
  19. func f2() {
  20. fmt.Println("这是f2")
  21. }
  22. func f3() {
  23. fmt.Println("这是f3")
  24. }
  25. /*
  26. map[1:0x2a5ac0 2:0x2a5b60 3:0x2a5c00] //整形都被映射到函数地址。
  27. 索引是[2] 内容是:这是f2
  28. 索引是[3] 内容是:这是f3
  29. 索引是[1] 内容是:这是f1
  30. */

1.1.1.2 用切片作为map的值

既然一个key只能对应一个value,而value又是一个原始类型,那么如果一个key要对应多个值怎么办?例如,当我们要处理unix机器上的所有进程,以父进程(pid为整形)作为key,所有的子线程(以所有子线程的pid组成的切片)作为value。通过将value定义为[]int类型或者其他类型的切片,就可以优雅的解决这个问题。
比如如下示例:

  1. map1 := make(map[int][]int)
  2. map2 := make(map[int]*[]int)

1.1.2 遍历map

使用range函数进行遍历

  1. countryCapitalMap := make(map[string]string)
  2. countryCapitalMap["France"] = "Paris"
  3. countryCapitalMap["Italy"] = "Rome"
  4. countryCapitalMap["Japan"] = "Tokyo"
  5. countryCapitalMap["India"] = "new Delhi"
  6. fmt.Println(countryCapitalMap)
  7. // 使用key输出map的值
  8. for country := range countryCapitalMap {
  9. fmt.Println("Capital of", country, "is", countryCapitalMap[country])
  10. }
  11. // 也可以直接输出键值对
  12. for k, v := range countryCapitalMap {
  13. fmt.Println(k, v)
  14. }
  15. /*
  16. map[France:Paris India:new Delhi Italy:Rome Japan:Tokyo]
  17. Capital of France is Paris
  18. Capital of Italy is Rome
  19. Capital of Japan is Tokyo
  20. Capital of India is new Delhi
  21. Capital of United Stated is not present
  22. France Paris
  23. Italy Rome
  24. Japan Tokyo
  25. India new Delhi
  26. */

1.1.3 判断键是否存在

我们可以通过key获取map中对应的value值。语法为:

  1. map[key]

但是当key如果不存在的时候,我们会得到该value值类型的默认值,比如string类型得到空字符串,int类型得到0。但是程序不会报错。
所以我们可以使用ok-idiom获取值,可知道key/value是否存在,语法为:

  1. value, ok := map[key]

示例:

  1. countryCapitalMap := make(map[string]string)
  2. countryCapitalMap["France"] = "Paris"
  3. countryCapitalMap["Italy"] = "Rome"
  4. countryCapitalMap["Japan"] = "Tokyo"
  5. countryCapitalMap["India"] = "new Delhi"
  6. fmt.Println(countryCapitalMap)
  7. // 查看元素在集合中是否存在
  8. capital, ok := countryCapitalMap["United States"]
  9. if ok {
  10. fmt.Println("Capital of United Stated is", capital)
  11. } else {
  12. fmt.Println("Capital of United Stated is not present")
  13. }
  14. /*
  15. map[France:Paris India:new Delhi Italy:Rome Japan:Tokyo]
  16. Capital of United Stated is not present
  17. */

1.1.4 delete()函数

可以通过delete()函数,通过键名删除数据

  1. countryCapitalMap := make(map[string]string)
  2. countryCapitalMap["France"] = "Paris"
  3. countryCapitalMap["Italy"] = "Rome"
  4. countryCapitalMap["Japan"] = "Tokyo"
  5. countryCapitalMap["India"] = "new Delhi"
  6. fmt.Println("原始 map")
  7. for country := range countryCapitalMap {
  8. fmt.Println("Capital of", country, "is", countryCapitalMap[country])
  9. }
  10. // delete()函数
  11. delete(countryCapitalMap, "France")
  12. fmt.Println("删除元素后")
  13. for country := range countryCapitalMap {
  14. fmt.Println("Capital of", country, "is", countryCapitalMap[country])
  15. }
  16. /*
  17. 原始 map
  18. Capital of France is Paris
  19. Capital of Italy is Rome
  20. Capital of Japan is Tokyo
  21. Capital of India is new Delhi
  22. 删除元素后
  23. Capital of Italy is Rome
  24. Capital of Japan is Tokyo
  25. Capital of India is new Delhi
  26. */

1.1.5 获取map的长度

可以通过len()函数获取map的长度

  1. countryCapitalMap := make(map[string]string)
  2. countryCapitalMap["France"] = "Paris"
  3. countryCapitalMap["Italy"] = "Rome"
  4. countryCapitalMap["Japan"] = "Tokyo"
  5. countryCapitalMap["India"] = "new Delhi"
  6. fmt.Println("原始 map")
  7. fmt.Println("countryCapitalMap length is ", len(countryCapitalMap))
  8. /*
  9. map[France:Paris India:new Delhi Italy:Rome Japan:Tokyo]
  10. countryCapitalMap length is 4
  11. */

1.1.6 map是引用类型

map是引用类型,当对map引用的对象进行修改时,因为都是指向相同的数据结构,所以原值也会发生变化

  1. countryCapitalMap := make(map[string]string)
  2. countryCapitalMap["France"] = "Paris"
  3. countryCapitalMap["Italy"] = "Rome"
  4. countryCapitalMap["Japan"] = "Tokyo"
  5. countryCapitalMap["India"] = "new Delhi"
  6. fmt.Println("countryCapitalMap原值是: ", countryCapitalMap)
  7. newCountryCapitalMap := countryCapitalMap
  8. newCountryCapitalMap["China"] = "Beijing" // 新增
  9. newCountryCapitalMap["France"] = "newParis" // 修改
  10. fmt.Println("countryCapitalMap现值是:", countryCapitalMap)
  11. /*
  12. countryCapitalMap原值是: map[France:Paris India:new Delhi Italy:Rome Japan:Tokyo]
  13. countryCapitalMap现值是: map[China:Beijing France:newParis India:new Delhi Italy:Rome Japan:Tokyo]
  14. */

map不能使用==操作符进行比较。==只能用来检查map是否为空。否则会报错:invalid operation: map1 == map2 (map can only be comparedto nil)

1.1.7 map容量

和数组不同,map会根据新增的key-value对动态的伸缩,因此它不存在固定的长度或者最大限制。但是你也可以选择标名map的初始容量capacity,格式为:

  1. make(map[keytype]valuetype,cap)

当map增长到容量上限的时候,如果再增加新的key-value对,map的大小会自动加1。所以出于性能的考虑,对于大的map或者会快速扩张的map,即使值是大概知道容量,也最好事先标明

1.1.8 map类型的切片

假设我们想获取一个map类型的切片,我们必须使用两次make()函数,第一次分配切片,第二次分配切片中的每个元素。
示例:

  1. func main() {
  2. mapStyleSlice()
  3. }
  4. // --------------------------map类型的切片---------------------
  5. func mapStyleSlice() {
  6. items := make([] map[int]string, 5) // 分配长度为5的切片
  7. for index := range items {
  8. // 通过索引获取切片,然后给该切片分配map元素
  9. items[index] = make(map[int]string, 1)
  10. items[index][1] = "test"
  11. }
  12. fmt.Printf("Version A:value of items:%v\n", items)
  13. }
  14. /*
  15. Version A:value of items:[map[1:test] map[1:test] map[1:test] map[1:test] map[1:test]]
  16. */

1.1.9 map的排序

map默认是无序的,不管是按照key还是按照value默认都是不排序的。
如果想要map排序,需要将key(或者value)拷贝到一个切片,再对切片排序(使用sort包 ),然后再通过遍历切片通过key获取值。

1.1.9.1 通过key进行排序

  1. func main() {
  2. mapSortByKey()
  3. }
  4. func mapSortByKey() {
  5. countryMap := make(map[string]int, 3)
  6. countryMap["D"] = 3
  7. countryMap["A"] = 2
  8. countryMap["C"] = 1
  9. fmt.Println("countryMap未排序前:")
  10. for key, value := range countryMap {
  11. fmt.Println("key是[", key, "] 值是:", value)
  12. }
  13. // 想要排序,需要将key或者value拷贝到一个切片,在进行切片排序,然后循环
  14. // 这里通过key进行排序
  15. keys := make([]string, len(countryMap))
  16. index := 0 // 从索引0开始设置值
  17. for key, _ := range countryMap {
  18. keys[index] = key // 将key放入切片
  19. index++ // 索引自增
  20. }
  21. fmt.Println("键名keys未排序前", keys)
  22. sort.Strings(keys)
  23. fmt.Println("键名keys排序后", keys)
  24. fmt.Println("countryMap排序后:")
  25. for _, k := range keys { // 遍历排序后的键
  26. fmt.Println("key是[", k, "] 值是:", countryMap[k])
  27. }
  28. }
  29. /*
  30. countryMap未排序前:
  31. key是[ D ] 值是: 3
  32. key是[ A ] 值是: 2
  33. key是[ C ] 值是: 1
  34. 键名keys未排序前 [C D A]
  35. 键名keys排序后 [A C D]
  36. countryMap排序后:
  37. key是[ A ] 值是: 2
  38. key是[ C ] 值是: 1
  39. key是[ D ] 值是: 3
  40. */

1.1.9.2 通过value进行排序

  1. func main() {
  2. mapSortByValue()
  3. }
  4. // --------------------------map通过value排序----------------------
  5. func mapSortByValue() {
  6. countryMap := make(map[string]int, 3)
  7. countryMap["D"] = 33
  8. countryMap["A"] = 65
  9. countryMap["C"] = 21
  10. countryMap["E"] = 8
  11. countryMap["F"] = 44
  12. fmt.Println("countryMap未排序前:")
  13. for key, value := range countryMap {
  14. fmt.Println("key是[", key, "] 值是:", value)
  15. }
  16. values := make([]int, len(countryMap))
  17. index := 0
  18. for _, value := range countryMap {
  19. values[index] = value
  20. index++
  21. }
  22. fmt.Println("值values未排序前 ", values)
  23. sort.Ints(values)
  24. fmt.Println("值values未排序后 ", values)
  25. fmt.Println("countryMap排序后:")
  26. for _, v := range values {
  27. for key, value := range countryMap {
  28. if v == value {
  29. fmt.Println("key是[", key, "] 值是:", value)
  30. }
  31. }
  32. }
  33. }
  34. /*
  35. countryMap未排序前:
  36. key是[ D ] 值是: 33
  37. key是[ A ] 值是: 65
  38. key是[ C ] 值是: 21
  39. key是[ E ] 值是: 8
  40. key是[ F ] 值是: 44
  41. 值values未排序前 [21 8 44 33 65]
  42. 值values未排序后 [8 21 33 44 65]
  43. countryMap排序后:
  44. key是[ E ] 值是: 8
  45. key是[ C ] 值是: 21
  46. key是[ D ] 值是: 33
  47. key是[ F ] 值是: 44
  48. key是[ A ] 值是: 65
  49. */

1.1.9.3 通过结构体和切片进行排序

综述上面两个示例可以得出,如果想要一个排序的列表,最好是使用类似下面的结构体切片,这样会比较有效。

  1. type name struct{
  2. key string
  3. value int
  4. }

需要注意的是,定义的结构体需要有对应的结构体函数(Len()、Swap()、Less()),才能进行比较,下面是一个通过value进行排序的示例:
如果想通过key进行排序,只需要修改less()方法中的return逻辑即可,下列代码有提示。

  1. type country struct {
  2. key string
  3. value int
  4. }
  5. type countrySlice []country
  6. func (cs countrySlice) Len() int {
  7. return len(cs)
  8. }
  9. func (cs countrySlice) Swap(i, j int) {
  10. cs[i], cs[j] = cs[j], cs[i]
  11. }
  12. func (cs countrySlice) Less(i, j int) bool {
  13. // 这里是比较value进行排序的,如果想通过key进行排序只需要修改下面的代码为 return cs[i].key < cs[j].key
  14. return cs[i].key < cs[j].key
  15. }
  16. func main() {
  17. mapSortByStructSlice()
  18. }
  19. // --------------------------使用结构体切片进行排序-----------------
  20. func mapSortByStructSlice() {
  21. countrySlice := make(countrySlice, 4)
  22. countrySlice[0] = country{"D", 1}
  23. countrySlice[1] = country{"A", 2}
  24. countrySlice[2] = country{"C", 4}
  25. countrySlice[3] = country{"B", 3}
  26. fmt.Println("未排序前country ", countrySlice)
  27. sort.Sort(countrySlice)
  28. fmt.Println("通过value排序后country ", countrySlice)
  29. }
  30. /*
  31. 未排序前country [{D 1} {A 2} {C 4} {B 3}]
  32. 通过value排序后country [{D 1} {A 2} {B 3} {C 4}]
  33. */

1.1.10 map键值对调

这里说的是调换key和value。如果map的值类型可以作为key且所有的value是唯一的,那么可以通过下面的方法进行简单的简直对调。

  1. func main() {
  2. kvReverse()
  3. }
  4. // --------------------------将map的键值对调----------------------
  5. func kvReverse() {
  6. countryMap := make(map[string]int, 3)
  7. countryMap["D"] = 33
  8. countryMap["A"] = 65
  9. countryMap["C"] = 21
  10. countryMap["E"] = 8
  11. countryMap["F"] = 44
  12. fmt.Println("未反转的值:", countryMap)
  13. reverseMap := make(map[int]string, len(countryMap))
  14. for k, v := range countryMap {
  15. reverseMap[v] = k
  16. }
  17. fmt.Println("反转的值:", reverseMap)
  18. }
  19. /*
  20. 未反转的值: map[A:65 C:21 D:33 E:8 F:44]
  21. 反转的值: map[8:E 21:C 33:D 44:F 65:A]
  22. */