《Go程序设计语言》学习笔记之map

《Go程序设计语言》学习笔记之map

 

一. 环境

  Centos8.5, go1.17.5 linux/amd64

 

二. 概念

1) 散列表,是一个拥有键值对元素的无序集合。在这个集合中,键是唯一的,键对应的值可以通过键来获取、更新或移除。在Go语言中,map 是散列表的引用,map 的类型是 map[k]v ,其中 k 和 v 是字典的键和值对应的数据类型。

map 中所有的键都拥有相同的类型,同时所有的值也都拥有相同的数据类型,但是键的类型和值的类型不一定相同。

2) 键的类型 k,必须是可以通过操作符 == 来进行比较的数据类型,所以 map 可以检测某一个键是否已经存在。

 

三. 声明/初始化

方式一. 使用 make,声明一个 map 变量 ages,此时,它会初始化为一个空 map。

  8 func main() {
  9     ages := make(map[string]int)
 10
 11     fmt.Println(ages == nil)
 12     fmt.Println(ages)
 13 }

  运行结果如下,此时 ages 是一个空 map,并非零值 nil。

声明新的空 map 的另一种方式,如下

  8 func main() {
  9     ages := map[string]int{}
 10
 11     fmt.Println(ages == nil)
 12     fmt.Println(ages)
 13 }

  运行结果如下

 

 方式二. 使用 map 的字面量来新建一个带初始化键值对元素的字典。注意第11行代码,如果大括号 } 换行,则第11行后面须有逗号,否则不用加逗号。

  8 func main() {
  9     ages := map[string]int{
 10         "Alice": 31,
 11         "Bog":   34,
 12     }
 13
 14     fmt.Println(ages)
 15 }

  运行结果如下

 

 这个与下面等价

  8 func main() {
  9     ages := make(map[string]int)
 10     ages["Alice"] = 31
 11     ages["Bob"] = 34
 12
 13     fmt.Println(ages)
 14 }

  运行结果如下

 

 四. 访问

1) 更新键对应的值

  8 func main() {
  9     ages := map[string]int{
 10         "Alice": 31,
 11         "Bob":   34,
 12     }
 13
 14     ages["Alice"] = 30
 15
 16     fmt.Printf("age: %d\n", ages["Alice"])
 17 }

  运行结果如下

 

 2) 删除指定的元素

  8 func main() {
  9     ages := map[string]int{
 10         "Alice": 31,
 11         "Bob":   34,
 12     }
 13
 14     delete(ages, "Alice")
 15
 16     fmt.Println(ages)
 17 }

  运行结果如下

 

 3) 遍历

a. 结合 range 关键字遍历

  8 func main() {
  9     ages := map[string]int{
 10         "Alice": 31,
 11         "Bob":   34,
 12         "Smith": 23,
 13     }
 14
 15     for k, v := range ages {
 16         fmt.Printf("%5s: %d\n", k, v)
 17     }
 18 }

  运行结果如下

 

 b. 按照指定的顺序遍历 map 中的元素。第19行代码,只需要 ages 中所有键,忽略了循环中的第二个变量。第23行代码,按照指定的顺序将 names 中的元素进行排序,然后按照这个顺序遍历 ages 中的值。

  9 func main() {
 10     ages := map[string]int{
 11         "David": 18,
 12         "Smith": 23,
 13         "Alice": 31,
 14         "Bob":   34,
 15     }
 16
 17     names := make([]string, 0, len(ages))
 18
 19     for k := range ages {
 20         names = append(names, k)
 21     }
 22
 23     sort.Strings(names)
 24
 25     for _, name := range names {
 26         fmt.Printf("%5s: %d\n", name, ages[name])
 27     }
 28 }

  运行结果如下

 

五. 零值与判断元素是否存在

1) map 的零值是 nil,即没有引用任何散列表。

  8 func main() {
  9     var ages map[string]int
 10
 11     fmt.Println(ages)
 12     fmt.Println(nil == ages)
 13     fmt.Printf("len: %d\n", len(ages))
 14 }

  运行结果如下

 

 许多操作可以在安全地在 map 的零值 nil 上执行,如查找元素、删除元素、获取 map 元素的个数(len),执行 range 循环,这些和空 map 的行为一致。

但是是向零值 map 中设置元素会导致错误。所以,设置元素前,必须初始化 map。

  4 func main() {
  5     var ages map[string]int
  6
  7     ages["Alice"] = 27
  8 }

  运行结果如下

 

 2) 判断元素是否存在

通过下标的方式访问 map 中的元素总是会有值。如果键在 map 中,你将得到键对应的值;如果键不在 map 中,你将得到 map 值类型的零值。

有时,需要知道一个元素是否在 map 中。如果元素的类型是数值类型,我们就需要分辨一个不存在的元素和恰好这个元素的值是0的情况,因为 map[k]均为0。方式如下

  8 func main() {
  9     ages := make(map[string]int)
 10
 11     ages["Alice"] = 27
 12
 13     if value, ok := ages["Bog"]; !ok {
 14         fmt.Println("not exist")
 15     } else {
 16         fmt.Printf("%d\n", value)
 17     }
 18 }

  如第13行代码这种方式访问 map 中的元素输出两个值,第二个值是一个布尔值,用来报告该元素是否存在。这个布尔变量一般叫作 ok,尤其是它立即用在 if 条件判断中的时候。 

  运行结果如下

 

 六. 比较

1) 和 slice 一样,map 不可直接进行比较,唯一合法的直接比较就是和 nil 做比较。为了判断两个 map 是否拥有相同的键和值,必须要写一个循环。

2) 当遇到不可直接比较的键类型,而又需要进行比较时,可以进行自定义一种比较方法来实现。

 

七. 实现集合类型的功能

Go没有提供集合类型,因为键都是唯一的,可以通过 map 来实现这一功能。比如使用 map[string]bool 的方式描述一个简单的集合。

 

八. 注意

无法获取 map 元素的地址。

  4 func main() {
    5     ages := make(map[string]int)
    6
    7     ages["Alice"] = 27
    8
>>  9     _ := &ages["Alice"]
   10 }

  运行结果如下,编译报错 “无法获取 map 元素的地址”。原因:由于 map 的增长可能会导致已有元素被重新散列到新的存储位置,这样就可能使得获取的地址无效。

posted @ 2022-01-03 22:04  bruce628  阅读(51)  评论(0编辑  收藏  举报