美文网首页Golang基础gin
Golang基础(二)-- map用法和本质

Golang基础(二)-- map用法和本质

作者: 魔改谢馒头 | 来源:发表于2018-08-22 19:29 被阅读42次

    本文转自:https://www.cnblogs.com/demon89/p/7259724.html

    一、Map的用法和本质

    Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
    Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

    所以在golang中Map的遍历不像其他语言一样,它的输出是无序的
    func traversal() {
        tmap := make(map[int]string)
        tmap[0] = "a"
        tmap[1] = "b"
        tmap[2] = "c"
        tmap[3] = "d"
        tmap[4] = "e"
        tmap[5] = "f"
        tmap[6] = "g"
        tmap[7] = "h"
        for k, v := range tmap {
            fmt.Printf("k:%d,v:%s\n", k, v)
        }
    }
    output:
    k:2,v:c
    k:3,v:d
    k:4,v:e
    k:5,v:f
    k:6,v:g
    k:7,v:h
    k:0,v:a
    k:1,v:b
    

    原文老哥他就很想按顺序打印

    //创建map
    countryCapitalMap := map[string] string {"France":"Paris","Italy":"Rome","Japan":"Tokyo","India":"New Delhi"}
    country_array := [] string {"France","Italy","Japan","India"}
    
     //根据自定义的数组的顺序有序的打印map中的信息
    for _,country := range country_array{
        fmt.Printf("Capital %v of  is %v \n",country,countryCapitalMap[country])
    }
    

    然后看到老哥这么倔强,我就顺道看了一下如何排序一个map
    其实思路还是一样的,只能用slice曲线救国。

    func main() {
        m := map[string]int{
            "something": 10,
            "yo":        20,
            "blah":      20,
        }
    
        type kv struct {
            Key   string
            Value int
        }
    
        var ss []kv
        for k, v := range m {
            ss = append(ss, kv{k, v})
        }
    
        sort.Slice(ss, func(i, j int) bool {
            return ss[i].Value > ss[j].Value  // 降序
            // return ss[i].Value < ss[j].Value  // 升序
        })
    
        for _, kv := range ss {
            fmt.Printf("%s, %d\n", kv.Key, kv.Value)
        }
    }
    
    定义 Map

    可以使用内建函数 make 也可以使用 map 关键字来定义 Map:

    声明变量,默认map是nil
    var map_name = map[type]type
    另外一种使用make创建
    map_name := make(map[type]type)
    

    如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对

    个人认为常用且值得注意的一点是,判断key是否存在于map

    //查看元素是否在map中,变量ok会返回true或者false,
    //当查询的key在map中则返回true并且captial会获取到map中的值
    captial, ok := countryCapitalMap["United States"]
    if ok{
        fmt.Println("Capital of",captial,"is",countryCapitalMap[captial])
    }else{
        fmt.Println("Capital of United States is not present")
    }
    

    二、高并发下的Map

    (我在找资料的时候发现了一篇神奇的文章:https://blog.csdn.net/m0_37579159/article/details/79344007
    文章不仅写了Map怎么线程安全,还从源码的角度写了怎么实现一个线程安全的Map,分析的很透彻,有时间接着看)

    高并发下Map的安全是一大问题,如果由多协程同时读和写就会出现 fatal error:concurrent map read and map write的错误,那么如何解决线程安全问题,我目前想到的就是三种解决思路:

    1.Lock(读写锁)
    
    import "sync"
     
    type Info struct {
        age int
    }
    type AccountMap struct {
        accounts map[string]*Info
        mutex    sync.Mutex
    }
     
    func NewAccountMap() *AccountMap {
        return &AccountMap{
            accounts: make(map[string]*Info),
        }
    }
    func (p *AccountMap) add(name string, age int) {
        p.mutex.Lock()
        defer p.mutex.Unlock()
        p.accounts[name] = &Info{age}
    }
    func (p *AccountMap) del(name string) {
        p.mutex.Lock()
        defer p.mutex.Unlock()
        delete(p.accounts, name)
    }
    func (p *AccountMap) find(name string) *Info {
        p.mutex.Lock()
        defer p.mutex.Unlock()
        res, ok := p.accounts[name]
        if !ok {
            return nil
        }
        inf := *res
        return &inf
    
    2.channel串行

    转自:https://blog.csdn.net/erlib/article/details/44195483
    我的理解:添加一个无缓存的chan,且在构造函数中添加for方法。在循环中无缓存的chan一次只能执行一个任务,所以不会发生并发读写错误。

    type Info struct {
        age int
    }
    type AccountMap struct {
        accounts map[string]*Info
        ch       chan func()
    }
     
    func NewAccountMap() *AccountMap {
        p := &AccountMap{
            accounts: make(map[string]*Info),
            ch:       make(chan func()),
        }
        go func() {
            for {
                (<-p.ch)()
            }
        }()
        return p
    }
    func (p *AccountMap) add(name string, age int) {
        p.ch <- func() {
            p.accounts[name] = &Info{age}
        }
    }
    func (p *AccountMap) del(name string) {
        p.ch <- func() {
            delete(p.accounts, name)
        }
    }
    func (p *AccountMap) find(name string) *Info {
        // 每次查询都要创建一个信道
        c := make(chan *Info)
        p.ch <- func() {
            res, ok := p.accounts[name]
            if !ok {
                c <- nil
            } else {
                inf := *res
                c <- &inf
            }
        }
        return <-c
    
    
    3.sync.map

    转自:https://blog.csdn.net/ChamPly/article/details/77622328
    所以说自从Go1.9版本以后就不用再担心这种并发问题了

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    type userInfo struct {
        Name string
        Age  int
    }
    
    var m sync.Map
    
    func main() {
    
        vv, ok := m.LoadOrStore("1", "one")
        fmt.Println(vv, ok) //one false
    
        vv, ok = m.Load("1")
        fmt.Println(vv, ok) //one true
    
        vv, ok = m.LoadOrStore("1", "oneone")
        fmt.Println(vv, ok) //one true
    
        vv, ok = m.Load("1")
        fmt.Println(vv, ok) //one true
    
        m.Store("1", "oneone")
        vv, ok = m.Load("1")
        fmt.Println(vv, ok) // oneone true
    
        m.Store("2", "two")
        m.Range(func(k, v interface{}) bool {
            fmt.Println(k, v)
            return true
        })
    
        m.Delete("1")
        m.Range(func(k, v interface{}) bool {
            fmt.Println(k, v)
            return true
        })
    
        map1 := make(map[string]userInfo)
        var user1 userInfo
        user1.Name = "ChamPly"
        user1.Age = 24
        map1["user1"] = user1
    
        var user2 userInfo
        user2.Name = "Tom"
        user2.Age = 18
        m.Store("map_test", map1)
    
        mapValue, _ := m.Load("map_test")
    
        for k, v := range mapValue.(interface{}).(map[string]userInfo) {
            fmt.Println(k, v)
            fmt.Println("name:", v.Name)
        }
    }
    

    相关文章

      网友评论

        本文标题:Golang基础(二)-- map用法和本质

        本文链接:https://www.haomeiwen.com/subject/ouoyiftx.html