Golang实践录:map的几个使用示例

Posted 李迟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang实践录:map的几个使用示例相关的知识,希望对你有一定的参考价值。

本文针对 Golang 的 Map 实现几个简单示例。这些都是在实际工程中使用到的。

基本使用

map 是一种无序的基于key-value的数据结构,Golang 的 map 是引用类型,因此必须初始化才能使用。

下面给出几种初始化形式示例:

var m map[string]int
m = make(map[string]int)
​
m := make(map[string]int)
​
var FuncMap = map[string]func(int, string)
...

​
var allMap map[string]PersonInfo_t
allMap = make(map[string]PersonInfo_t)

增加:

m["latelee"] = 250

删除:

delete(m, "latelee")

查询 key 是否存在:

if v, ok := allMap[id]; ok 
...

遍历:

for k, v := range allMap 
    ...

实践

对基本使用有一定了解后,需根据实际应用场合使用,本节列举几个示例:

函数调用

需求:一个命令模块,根据不同的命令名称,调用不同的函数。

可用数组实现,但要遍历查询;也可用if判断,如果用 map 实现,从代码整洁和维护角度看,都是个不错的选择。定义的FuncMap,key为命令,value为函数指针,具体代码如下:

// 全局变量,映射,可由其它包使用
// 简单定义map的函数指针
var FuncMap = map[string]func(int, string)
    "111": map1,
    "222": map2,

​
func map1(a int, b string) 
    fmt.Println("111", a, b)

​
func map2(a int, b string) 
    fmt.Println("222", a, b)

​
func test1(id string) 
    a := 250
    b := "25.250"
    if fn, ok := FuncMap[id]; ok 
        fn(a, b)
     else 
        fmt.Println(id, "not found func")
    

输出结果:

111 250 25.250
222 250 25.250
333 not found func

多线程的使用

需求:工程中有较多线程(Golang中实际上协程),对同一个 map 有读、写的操作,此时,普通的 map 则无法适应,需使用sync.Map,使用也简单,主要函数有:

添加:g_syncMap.Store

删除:g_syncMap.Delete(key)

测试代码如下:

// sync.Map使用
​
var g_syncMap sync.Map
​
func showSyncMap() 
    // 遍历打印,不做死循环
    g_syncMap.Range(func(k, v interface) bool 
        v1 := v.(string)
        fmt.Printf("key: %v -> %v\\n", k, v1)
        return true
    )

​
func test2() 
    // 存
    g_syncMap.Store(1, "111")
    g_syncMap.Store(2, "222")
    g_syncMap.Store(3, "333")
    showSyncMap()
​
    // 删除
    g_syncMap.Delete(1)
    showSyncMap()
​
    g_syncMap.Store(3, "333_111") // 重复写同一个key,会更新为新的
    showSyncMap()
​
    //Load 方法,获得value
    if v, ok := g_syncMap.Load(2); ok 
        fmt.Println("Load ->", v)
    
    //LoadOrStore方法,获取或者保存
    //参数是一对key:value,如果该key存在且没有被标记删除则返回原先的value(不更新)和true;
    // 不存在则store,返回该value 和false
    if vv, ok := g_syncMap.LoadOrStore(1, "c"); !ok  // 前面删除1了,这里重新保存
        fmt.Println("save new", vv)
    
    if vv, ok := g_syncMap.LoadOrStore(2, "c"); ok  // 2 一直存在
        fmt.Println("exist", vv, ok)
    
    showSyncMap()

输出结果如下:

key: 1 -> 111
key: 2 -> 222
key: 3 -> 333
key: 2 -> 222
key: 3 -> 333
key: 2 -> 222
key: 3 -> 333_111
Load -> 222
save new c
exist 222 true
key: 1 -> c
key: 2 -> 222
key: 3 -> 333_111

多 key 单独查询

需求:某种数据有很多个字段,可以根据其中的一些字段查询的对象。如某一人员信息,有ID、code代码、名称等,可以根据ID查人员信息,也可以根据code代码查,还可以根据名称查(假定名称不重复)。

由于 map 不能有多个 key,因此使用多个 map 映射。设主信息的 allMap 的 key 为 ID,value 为人员信息。另外有若干辅助 map,cMap 的 key 为 code,value为ID;hMap类似,key 为 hex 码,value 为 ID。查询时,先查辅助 map,得到 ID值,再查主 map。

示例代码:

type PersonInfo_t struct 
    id   string
    hex  string
    code string
    name string
    age  int

​
var allMap map[string]PersonInfo_t
var cMap map[string]string
var hMap map[string]string
​
func findFromItem(item string) (ret PersonInfo_t) 
    id := item // default id
​
    if len(item) == 4  // hex
        id = hMap[item]
     else if len(item) == 6  // code
        id = cMap[item]
    
​
    if v, ok := allMap[id]; ok 
        fmt.Printf("found %v\\n", id)
        return v
    
​
    return PersonInfo_t

​
func showMap(allMap map[string]PersonInfo_t) 
    var keys []string
    for k, _ := range allMap 
        keys = append(keys, k)
    
    // 进行数组的排序
    sort.Strings(keys)
    // 遍历数组就是有序的了
    for _, k := range keys 
        fmt.Printf("v: %v\\n", allMap[k])
    

​
func multiMap() 
    allMap = make(map[string]PersonInfo_t)
    cMap = make(map[string]string)
    hMap = make(map[string]string)
    for i := 0; i < 10; i++ 
        var tmp PersonInfo_t
        tmp.id = fmt.Sprintf("id_%02d", i)
        tmp.code = fmt.Sprintf("C00%03d", i)
        tmp.hex = fmt.Sprintf("H%03d", i)
        tmp.name = fmt.Sprintf("foo_%d_bar", i)
        tmp.age = 20
        // 总的
        allMap[tmp.id] = tmp
​
        // 存储id的
        cMap[tmp.code] = tmp.id
        hMap[tmp.hex] = tmp.id
    
​
    //showMap(allMap)
​
    id := "id_00"
    fmt.Printf("%v: %v\\n", id, allMap[id])
​
    id = "id_100"
    fmt.Printf("%v: %v\\n", id, allMap[id])
​
    id = "H007"
    fmt.Printf("%v: %v\\n", id, findFromItem(id))
​
    id = "H017"
    fmt.Printf("%v: %v\\n", id, findFromItem(id))
​
    id = "C00005"
    fmt.Printf("%v: %v\\n", id, findFromItem(id))
​

输出结果:

id_00: id_00 H000 C00000 foo_0_bar 20
id_100:     0
found id_07
H007: id_07 H007 C00007 foo_7_bar 20
H017:     0
found id_05
C00005: id_05 H005 C00005 foo_5_bar 20
​

多 key 查询

需求:某种数据有很多个字段,由其中的两个字段共同决定查询的对象。接上一例子,假定必须有code代码和hex代码才能确认人员信息。在设计上,将这两个字段合并作为key即可。

代码如下:

var mMap map[string]PersonInfo_t
​
func findFromM(code, hex string) (ret PersonInfo_t) 
    key := fmt.Sprintf("%v_%v", code, hex)
​
    if v, ok := mMap[key]; ok 
        return v
    
​
    return PersonInfo_t

​
func mkMap() 
    mMap = make(map[string]PersonInfo_t)
    for i := 0; i < 10; i++ 
        var tmp PersonInfo_t
        tmp.id = fmt.Sprintf("id_%02d", i)
        tmp.code = fmt.Sprintf("C00%03d", i)
        tmp.hex = fmt.Sprintf("H%03d", i)
        tmp.name = fmt.Sprintf("foo_%d_bar", i)
        tmp.age = 20
        // 多个key
        key := fmt.Sprintf("%v_%v", tmp.code, tmp.hex)
        mMap[key] = tmp
    
​
    showMap(allMap)
​
    code := "C00000"
    hex := "H000"
    fmt.Printf("%v & %v: %v\\n", code, hex, findFromM(code, hex))
    code = "C00000"
    hex = "H111" // 不存在的
    fmt.Printf("%v & %v: %v\\n", code, hex, findFromM(code, hex))

输出结果:

C00000 & H000: id_00 C00000 H000 foo_0_bar 20
C00000 & H111:     0

小结

对于多 key 单独查询的场合,是典型的用空间换时间的思路,如果使用时间换空间,则可建立数组,遍历查询,查询条件即为不同的字段,数量在万级别以下,可能没有明显差别,但到百万级别,耗时就比较大了,因此,是否真正需要应用 map,还得自行评估。实际上,笔者2种方式都有使用到。

以上是关于Golang实践录:map的几个使用示例的主要内容,如果未能解决你的问题,请参考以下文章

Golang实践录:map的几个使用示例

Golang实践录:查询数据表的几种方式

Golang实践录:查询数据表的几种方式

Golang实践录:ssh及scp实现的优化

Golang实践录:ssh及scp实现的优化

Golang实践录:ssh及scp实现的优化