golang知识点-map

map介绍

map类似于其他的语言的字典。用于存放一个key,value值对。定义的语法map[type of key][type of value]。

map的定义及初始化

通过向 make 函数传入键和值的类型,可以创建 map。make(map[type of key]type of value) 是创建 map 的语法。make是专门用来创建初始化map,slice,chan的函数。 
因为var或new在声明map之后,是没有对map再进行内存空间初始化。此时的map是一个零值,nil。相当于是map是一个指针类型值,而没有对这个指针值做初始化。

package main

import (
    "fmt"
) 
func main() {
    personSalary := make(map[string]int)
    personSalary["steve"] = 12000
    personSalary["jamie"] = 15000
    personSalary["mike"] = 9000
    fmt.Println("personSalary map contents:", personSalary)
}

map的属性

  • map是引用类型。
  • map的底层数据结构
  • map删除元素,不会释放内存

 

map的底层数据结构

map的底层结构是hmap(即hashmap的缩写),核心元素是一个由若干个桶(bucket,结构为bmap)组成的数组,每个bucket可以存放若干元素(通常是8个),key通过哈希算法被归入不同的bucket中。当超过8个元素需要存入某个bucket时,hmap会使用extra中的overflow来拓展该bucket


map的操作

  • map添加获取元素(判断key值是否存在)
  • 删除map元素
  • 遍历map
  • 获取map长度
  • map的判等操作
  • 线程安全map

添加&获取元素


package main
 
import (
    "fmt"
)
 
func main() {
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000        //增加元素值
    employee := "jamie"
    fmt.Println("Salary of", employee, "is", personSalary[employee])  //获取元素值
    value,ok := personSalary['nothing]   //获取元素值,做判等操作。 ok 是 true,表示 key 存在,key 对应的值就是 value ,反之表示 key 不存在
}

删除map元素


删除 map 中 key 的语法是 delete(map, key)。这个函数没有返回值。
package main
 
import (  
    "fmt"
)
 
func main() {  
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("map before deletion", personSalary)
    delete(personSalary, "steve")
    fmt.Println("map after deletion", personSalary)
 
}    

遍历map


package main
 
import (
    "fmt"
)
 
func main() {
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("All items of a map")
    for key, value := range personSalary {
        fmt.Printf("personSalary[%s] = %d\n", key, value)
    }
 
}
输出:
All items of a map
personSalary[mike] = 9000
personSalary[steve] = 12000
personSalary[jamie] = 15000

获取map长度


package main
 
import (
    "fmt"
)
 
func main() {
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("length is", len(personSalary))
 
}
输出:length is 3。

map的判等操作

因为map是一个指针类型。map当中可能存储不可比较的数据类型(如指针类型)。没办法通过==直接进行判等操作。
针对此类型的数据的判等操作可以使用reflect.DeepEqual(v1,v2)来进行判断。此时必须内部数据完全相等。包括指针指向的最终的数据的数值。 
查看以下示例:

package main
 
import (  
    "fmt"
    "reflect"
)
 
func main() {  
    a := make(map[string]string)
    a["one"] = "name1"
    a["two"] = "name2"
    a["three"] = "name3"
    b := map[string]string{
        "one": "name1",
        "two": "name2",
        "three": "name3",
    }
   if reflect.DeepEqual(a,b){
        fmt.Println("equal")
   }else{
        fmt.Println("no equal")

   }
   b["three"] = "name4"
   if reflect.DeepEqual(a,b){
        fmt.Println("change: equal")
   }else{
        fmt.Println("change: no equal")
   }

}
输出:
equal
change: no equal

非线程安全map

Go 1.6之前, 内置的map类型是部分goroutine安全的,并发的读没有问题,并发的写可能有问题。自go 1.6之后, 并发地读写map会报错,
,下列程序中一个goroutine一直读,一个goroutine一只写同一个键值,即即使读写的键不相同,而且map也没有"扩容"等操作,代码还是会报错。

package main
func main() {
    m := make(map[int]int)
    go func() {
        for {
            _ = m[1]
        }
    }()
    go func() {
        for {
            m[2] = 2
        }
    }()
    select {}
}
输出错误:
fatal error: concurrent map read and map write。

如何解决并发安全问题

但是,很多时候,我们会并发地使用map对象,尤其是在一定规模的项目中,map总会保存goroutine共享的数据。在Go官方blog的Go maps in action一文中,提供了一种简便的解决方案。

var counter = struct{
    sync.RWMutex
    m map[string]int
}{m: make(map[string]int)}

它使用嵌入struct为map增加一个读写锁。

读数据的时候很方便的加锁:
counter.RLock()
n := counter.m["some_key"]
counter.RUnlock()
fmt.Println("some_key:", n)

写数据的时候:
counter.Lock()
counter.m["some_key"]++
counter.Unlock()

以上的解决方案相当简洁,并且利用读写锁而不是Mutex可以进一步减少读写的时候因为锁带来的性能。在map的数据非常大的情况下,一把锁会导致大并发的客户端共争一把锁。

1.9使用sync.Map并发安全Map

操作方法

  • Load
  • Store
  • Delete
  • Range

详细的源码解读参考:https://colobu.com/2017/07/11/dive-into-sync-Map/

package main
 
import (
    "fmt"
    "sync"
)
 
func main() {
    //生命初始化一个安全map
    var m sync.Map
 
    //安全的操作存储值
    m.Store(1,"a")
    m.Store(2,"b")
 
    //LoadOrStore
    //若key不存在,则存入key和value,返回false和输入的value
    v,ok := m.LoadOrStore("1","aaa")
    fmt.Println(ok,v) //false aaa
 
    //若key已存在,则返回true和key对应的value,不会修改原来的value
    v,ok = m.LoadOrStore(1,"aaa")
    fmt.Println(ok,v) //false aaa
 
    //Load 获取元素值
    v,ok = m.Load(1)
    if ok{
        fmt.Println("it's an existing key,value is ",v)
    } else {
        fmt.Println("it's an unknown key")
    }
 
    //Range
    //遍历sync.Map, 要求输入一个func作为参数
    f := func(k, v interface{}) bool {
        //这个函数的入参、出参的类型都已经固定,不能修改
        //可以在函数体内编写自己的代码,调用map中的k,v
 
            fmt.Println(k,v)
            return true
        }
    m.Range(f)
 
    //Delete
    m.Delete(1)
    fmt.Println(m.Load(1)) 
}


小结:

  • map是存储一个key-value键值对。key值必须是值类型的。
  • map的定义方法: make(map[string]int); m := map[string]string{"a":"a","b":"b"}
  • map是引用类型值,底层是一个hash桶
  • map非并发安全
  • 并发的安全sync.Map。var m sync.Map。  m.Store("key","value"),m.Load("key"),m.Delete("key"),m.Range(func(k,v interface{})bool{ //range需要传入一个函数,在函数内部加入处理逻辑}) 

 

参考连接:
https://colobu.com/2017/07/11/dive-into-sync-Map/
 

转载于:https://my.oschina.net/u/4156317/blog/3071180

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值