go中map哈希表使用总结

作者:程序员CKeen
博客:http://ckeen.cn​​​​​​​​​​​​​​​​​​​​​

长期坚持做有价值的事!积累沉淀,持续成长,升维思考!希望把编码作为长期兴趣爱好


在 Go 语言中,map是一种基于哈希表实现的数据结构。哈希表是一种方便且功能强大的内置数据结构,它是一个无序的key/value键值对的集合。

哈希表一般写作map[K]V,其中K代表键,V代表K对应的值。哈希表通过给定的键Key可以在常数时间复杂度内检索、更新或删除对应的value,是一种比较常用的数据结构。

下面主要介绍一下map的具体的使用方法:

1.map的的几种创建方式

  • 直接创建一个空map​​​​​​​
    map1 := map[string]int {}       // 先创建一个空map,不包含任何键值对
    map1["foo"] = 1                 // 添加一个键值对
    map1["boo"] = 2
    map1["hello"] = 3
    fmt.Println(map1)
    // map[boo:2 foo:1 hello:3] ,可以看到打印的结果是无序的
  • 创建一个初始化键值对的map
  • map2 := map[string]int {       // 初始化创建一个包含键值对的map
        "foo": 1,
        "boo": 2,
        "hello" : 3,
    }
    fmt.Println(map2)
    // map[boo:2 foo:1 hello:3]

  • make内置函数创建。go中所有的引用类型都可以通过make来创建实例
    map3 := make(map[string]int)
    map3["foo"] = 1
    map3["boo"] = 2
    map3["bar"] = 3
    fmt.Println(map3)
    // map[bar:3 boo:2 foo:1]

2.map的操作

  1. 对map进行添加键值对操作
    map4 := make(map[string]int)
    map4["foo"] = 1
    map4["boo"] = 2
    map4["bar"] = 3
    map4["bar"] = 4
    fmt.Println(map4)
    // map[bar:4 boo:2 foo:1]
    创建一个map的集合对象后,只需要直接使用中括号添加key,然后=后面直接赋值即可。

    ​​​​​​​map的key/value使用需要注意的点
         1. ​​ ​​map的键可以是定义了相等运算符的任何类型,例如整数、浮点数和复数、字符串、指针、接口(只要动态类型支持相等)、结构体和数组。
         2. 切片不能用作映射键,因为它们没有定义相等。
         3. 虽然浮点数类型也是支持相等运算符比较的,但是将浮点数用做key类型,最坏的情况是可能出现的NaN和任何浮点数都不相等。
         4. map添加的时候如果该key已经存在,则会覆盖该键对应的值,而不会报错。
  • 对map进行取值操作
    map4 := make(map[string]int)
    map4["foo"] = 1
    map4["boo"] = 2
    map4["bar"] = 3
    map4["bar"] = 4
    barVal := map4["bar"]
    fmt.Println(map4,barVal)
    // map[bar:4 boo:2 foo:1] 4
    
    hello := map4["hello"]
    fmt.Println(hello)
    // 打印 0
    barVal2 := &map4["bar"]  //Cannot take the address of 'map4["bar"]'
    map中进行取值操作,如果key不存在的时候,它不会报错, 它会返回value的零值。而且map中的value值并不是一个变量,因此我们不能对map的元素进行取址操作
  • 对map的删除操作
    map4 := make(map[string]int)
    map4["foo"] = 1
    map4["boo"] = 2
    map4["bar"] = 3
    delete(map4,"bar")
    fmt.Println(map4)
    直接使用go的库函数delete就可以完成删除key/value操作,当key不存在的时候,delete操作也不会报错,也没有任何反馈信息。因此如果你想知道是否你删除的键值对是否有删除, 可以先判断该键值对是否存在。
  • 对map的遍历操作
    map6 := make(map[string]int)
    map6["foo"] = 1
    map6["boo"] = 2
    map6["bar"] = 3
    map6["hello"] = 5
    for k, val := range map6 {
    	fmt.Printf("key:%v,value:%v\n",k,val)
    }
    // key:foo,value:1
    // key:boo,value:2
    // key:bar,value:3
    // key:hello,value:5
    遍历map中全部的key/value对,使用range风格的for循环实现,和之前的slice遍历语法类似。map的迭代顺序是不确定的,并且不同的哈希函数实现可能导致不同的遍历顺序。实际遍历的顺序是随机的,每一次遍历的顺序都不相同。

    如果我们想对map进行排序,则我们必须显式地对key使用slice进行排序,然后根据slice中key的顺序,去map中取值。
    var keys []string
    for key := range map4 {
    	keys = append(keys, key)
    }
    sort.Strings(keys)
    for _, val := range map4 {
        fmt.Printf("%s\t%d\n", val, ages[val])
    }
  • 判断map的key的是否存在
    map7 := map[string]int {   // 初始化创建
        "foo": 1,
        "boo": 2,
        "hello" : 3,
        "test" : 0 
    }
    fmt.Println(map7["test"],map7["world"])
    // 打印的为零值 0 0
    
    if val, ok := map7["world"]; ok {
    	fmt.Println(val)
    }
    // 不会打印结果
    当前map的键不存在的时候,取到的值默认为零值。这样就存在当取到为零值的时候,无法判断到底是键不存在,还是存得键本身就为零值的情况,所以go利用多返回值的形式,可以来判断是否。

    上述代码中,如果ok为true, 则说明对应的key存在。
    ​​​​​​​
    为了测试是否存在于映射中而不考虑实际值,可以使用空白标识符(_)来代替通常的值变量。

3.map中使用的时候要注意点

  • 与切片一样,map了保存对底层数据结构的引用。如果函数接受map参数,则调用者可以看到它对map元素所做的更改,类似于传递指针。
    我们如果不想函数内部修改影响传入的map结构时,先进行复制操作是比较好的做法。
    go中map哈希表使用总结_第1张图片

    同样如果我们结构内部对外的暴露的是一个map结构,如果不想外面的修改影响到内部的mpa结构,也可以将对外暴露的结构进行复制一个新的对外。
    go中map哈希表使用总结_第2张图片

     ​​​​​

  • 在使用 map 时,需要注意并发安全性。如果多个 goroutine 同时读写同一个 map,可能会导致数据竞争的问题。可以使用 sync 包中的锁机制来保证并发安全。也可以是用sync.Map结构
  • 在使用 map 时,需要注意内存占用。如果 map 中存储了大量的数据,可能会导致内存占用过高,从而影响程序的运行效率。

你可能感兴趣的:(golang,散列表,后端,开发语言)