gmap

支持并发安全开关选项的map容器,最常用的数据结构。

该模块包含多个数据结构的map容器:HashMapTreeMapListMap

类型 数据结构 平均复杂度 支持排序 有序遍历 说明
HashMap 哈希表 O(1) 高性能读写操作,内存占用较高,随机遍历
ListMap 哈希表+双向链表 O(2) 支持按照写入顺序遍历,内存占用较高
TreeMap 红黑树 O(log N) 内存占用紧凑,支持键名排序及有序遍历

此外,gmap模块支持多种以哈希表为基础数据结构的常见类型map定义:IntIntMapIntStrMapIntAnyMapStrIntMapStrStrMapStrAnyMap

使用场景

任何map/哈希表/关联数组使用场景,尤其是并发安全场景中。

使用方式

  1. import "github.com/gogf/gf/container/gmap"

接口文档

https://godoc.org/github.com/gogf/gf/container/gmap

并发安全

gmap支持并发安全选项开关,在默认情况下是非并发安全的,开发者可以选择开启gmap的并发安全特性(传递初始化开关参数safe参数值为true, 必须在初始化时设定,不能运行时动态设定)。如:

  1. m := gmap.New(true)

不仅仅是gmap模块,gf框架的其他并发安全数据结构也支持并发安全特性开关。

使用示例

基本使用

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/container/gmap"
  5. )
  6. func main() {
  7. // 创建一个默认的gmap对象,
  8. // 默认情况下该gmap对象不支持并发安全特性,
  9. // 初始化时可以给定true参数开启并发安全特性。
  10. m := gmap.New()
  11. // 设置键值对
  12. for i := 0; i < 10; i++ {
  13. m.Set(i, i)
  14. }
  15. // 查询大小
  16. fmt.Println(m.Size())
  17. // 批量设置键值对(不同的数据类型对象参数不同)
  18. m.Sets(map[interface{}]interface{}{
  19. 10 : 10,
  20. 11 : 11,
  21. })
  22. fmt.Println(m.Size())
  23. // 查询是否存在
  24. fmt.Println(m.Contains(1))
  25. // 查询键值
  26. fmt.Println(m.Get(1))
  27. // 删除数据项
  28. m.Remove(9)
  29. fmt.Println(m.Size())
  30. // 批量删除
  31. m.Removes([]interface{}{10, 11})
  32. fmt.Println(m.Size())
  33. // 当前键名列表(随机排序)
  34. fmt.Println(m.Keys())
  35. // 当前键值列表(随机排序)
  36. fmt.Println(m.Values())
  37. // 查询键名,当键值不存在时,写入给定的默认值
  38. fmt.Println(m.GetOrSet(100, 100))
  39. // 删除键值对,并返回对应的键值
  40. fmt.Println(m.Remove(100))
  41. // 遍历map
  42. m.Iterator(func(k interface{}, v interface{}) bool {
  43. fmt.Printf("%v:%v ", k, v)
  44. return true
  45. })
  46. // 自定义写锁操作
  47. m.LockFunc(func(m map[interface{}]interface{}) {
  48. m[99] = 99
  49. })
  50. // 自定义读锁操作
  51. m.RLockFunc(func(m map[interface{}]interface{}) {
  52. fmt.Println(m[99])
  53. })
  54. // 清空map
  55. m.Clear()
  56. // 判断map是否为空
  57. fmt.Println(m.IsEmpty())
  58. }

执行后,输出结果为:

  1. 10
  2. 12
  3. true
  4. 1
  5. 11
  6. 9
  7. [0 1 2 4 6 7 3 5 8]
  8. [3 5 8 0 1 2 4 6 7]
  9. 100
  10. 100
  11. 3:3 5:5 8:8 7:7 0:0 1:1 2:2 4:4 6:6 99
  12. true

有序遍历

我们来看一下三种不同类型map的有序性遍历示例。

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/frame/g"
  5. "github.com/gogf/gf/container/gmap"
  6. "github.com/gogf/gf/util/gutil"
  7. )
  8. func main() {
  9. array := g.Slice{2, 3, 1, 5, 4, 6, 8, 7, 9}
  10. hashMap := gmap.New(true)
  11. listMap := gmap.NewListMap(true)
  12. treeMap := gmap.NewTreeMap(gutil.ComparatorInt, true)
  13. for _, v := range array {
  14. hashMap.Set(v, v)
  15. }
  16. for _, v := range array {
  17. listMap.Set(v, v)
  18. }
  19. for _, v := range array {
  20. treeMap.Set(v, v)
  21. }
  22. fmt.Println("HashMap Keys:", hashMap.Keys())
  23. fmt.Println("HashMap Values:", hashMap.Values())
  24. fmt.Println("ListMap Keys:", listMap.Keys())
  25. fmt.Println("ListMap Values:", listMap.Values())
  26. fmt.Println("TreeMap Keys:", treeMap.Keys())
  27. fmt.Println("TreeMap Values:", treeMap.Values())
  28. }

执行后,输出结果为:

  1. HashMap Keys: [4 6 8 7 9 2 3 1 5]
  2. HashMap Values: [6 8 4 3 1 5 7 9 2]
  3. ListMap Keys: [2 3 1 5 4 6 8 7 9]
  4. ListMap Values: [2 3 1 5 4 6 8 7 9]
  5. TreeMap Keys: [1 2 3 4 5 6 7 8 9]
  6. TreeMap Values: [1 2 3 4 5 6 7 8 9]

FilterEmpty/FilterNil空值过滤

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/container/gmap"
  5. "github.com/gogf/gf/frame/g"
  6. )
  7. func main() {
  8. m1 := gmap.NewFrom(g.MapAnyAny{
  9. "k1": "",
  10. "k2": nil,
  11. "k3": 0,
  12. "k4": 1,
  13. })
  14. m2 := gmap.NewFrom(g.MapAnyAny{
  15. "k1": "",
  16. "k2": nil,
  17. "k3": 0,
  18. "k4": 1,
  19. })
  20. m1.FilterEmpty()
  21. m2.FilterNil()
  22. fmt.Println(m1.Map())
  23. fmt.Println(m2.Map())
  24. // Output:
  25. // map[k4:1]
  26. // map[k1: k3:0 k4:1]
  27. }

Flip键值对反转

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/container/gmap"
  5. "github.com/gogf/gf/frame/g"
  6. )
  7. func main() {
  8. var m gmap.Map
  9. m.Sets(g.MapAnyAny{
  10. "k1": "v1",
  11. "k2": "v2",
  12. })
  13. m.Flip()
  14. fmt.Println(m.Map())
  15. // May Output:
  16. // map[v1:k1 v2:k2]
  17. }

Keys/Values键名/数值列表

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/container/gmap"
  5. "github.com/gogf/gf/frame/g"
  6. )
  7. func main() {
  8. var m gmap.Map
  9. m.Sets(g.MapAnyAny{
  10. "k1": "v1",
  11. "k2": "v2",
  12. "k3": "v3",
  13. "k4": "v4",
  14. })
  15. fmt.Println(m.Keys())
  16. fmt.Println(m.Values())
  17. // May Output:
  18. // [k1 k2 k3 k4]
  19. // [v2 v3 v4 v1]
  20. }

Pop/Pops随机出栈

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/container/gmap"
  5. "github.com/gogf/gf/frame/g"
  6. )
  7. func main() {
  8. var m gmap.Map
  9. m.Sets(g.MapAnyAny{
  10. "k1": "v1",
  11. "k2": "v2",
  12. "k3": "v3",
  13. "k4": "v4",
  14. })
  15. fmt.Println(m.Pop())
  16. fmt.Println(m.Pops(2))
  17. fmt.Println(m.Size())
  18. // May Output:
  19. // k1 v1
  20. // map[k2:v2 k4:v4]
  21. // 1
  22. }

SetIfNotExist*判断性写入

判断性写入是指当指定的键名不存在时则写入并且方法返回true,否则忽略吸入并且方法返回false。相关方法如下:

  • SetIfNotExist
  • SetIfNotExistFunc
  • SetIfNotExistFuncLock

方法具体描述请查看接口文档或源码注释。

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/container/gmap"
  5. )
  6. func main() {
  7. var m gmap.Map
  8. fmt.Println(m.SetIfNotExist("k1", "v1"))
  9. fmt.Println(m.SetIfNotExist("k1", "v1"))
  10. fmt.Println(m.Map())
  11. // Output:
  12. // true
  13. // false
  14. // map[k1:v1]
  15. }

Merge哈希表合并

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/container/gmap"
  5. )
  6. func main() {
  7. var m1, m2 gmap.Map
  8. m1.Set("key1", "val1")
  9. m2.Set("key2", "val2")
  10. m1.Merge(&m2)
  11. fmt.Println(m1.Map())
  12. // May Output:
  13. // map[key1:val1 key2:val2]
  14. }

JSON序列化/反序列

gmap模块下的所有容器类型均实现了标准库json数据格式的序列化/反序列化接口。

  1. Marshal

    1. package main
    2. import (
    3. "encoding/json"
    4. "fmt"
    5. "github.com/gogf/gf/frame/g"
    6. "github.com/gogf/gf/container/gmap"
    7. )
    8. func main() {
    9. m := gmap.New()
    10. m.Sets(g.MapAnyAny{
    11. "name": "john",
    12. "score": 100,
    13. })
    14. b, _ := json.Marshal(m)
    15. fmt.Println(string(b))
    16. }

    执行后,输出结果:

    1. {"name":"john","score":100}
  2. Unmarshal

    1. package main
    2. import (
    3. "encoding/json"
    4. "fmt"
    5. "github.com/gogf/gf/container/gmap"
    6. )
    7. func main() {
    8. m := gmap.Map{}
    9. s := []byte(`{"name":"john","score":100}`)
    10. json.Unmarshal(s, &m)
    11. fmt.Println(m.Map())
    12. }

    执行后,输出结果:

    1. map[name:john score:100]

性能测试

并发安全

https://github.com/gogf/gf/blob/master/container/gmap/gmap_z_bench_safe_test.go

  1. goos: linux
  2. goarch: amd64
  3. Benchmark_IntIntMap_Set-4 10000000 202 ns/op 15 B/op 0 allocs/op
  4. Benchmark_IntAnyMap_Set-4 10000000 262 ns/op 29 B/op 1 allocs/op
  5. Benchmark_IntStrMap_Set-4 10000000 241 ns/op 22 B/op 0 allocs/op
  6. Benchmark_AnyAnyMap_Set-4 5000000 359 ns/op 40 B/op 2 allocs/op
  7. Benchmark_StrIntMap_Set-4 5000000 305 ns/op 26 B/op 1 allocs/op
  8. Benchmark_StrAnyMap_Set-4 5000000 354 ns/op 40 B/op 2 allocs/op
  9. Benchmark_StrStrMap_Set-4 5000000 338 ns/op 32 B/op 1 allocs/op
  10. Benchmark_IntIntMap_Get-4 20000000 86.6 ns/op 0 B/op 0 allocs/op
  11. Benchmark_IntAnyMap_Get-4 30000000 69.7 ns/op 0 B/op 0 allocs/op
  12. Benchmark_IntStrMap_Get-4 30000000 69.6 ns/op 0 B/op 0 allocs/op
  13. Benchmark_AnyAnyMap_Get-4 20000000 74.4 ns/op 0 B/op 0 allocs/op
  14. Benchmark_StrIntMap_Get-4 20000000 116 ns/op 7 B/op 0 allocs/op
  15. Benchmark_StrAnyMap_Get-4 20000000 92.3 ns/op 7 B/op 0 allocs/op
  16. Benchmark_StrStrMap_Get-4 20000000 91.9 ns/op 7 B/op 0 allocs/op

非并发安全

https://github.com/gogf/gf/blob/master/container/gmap/gmap_z_bench_unsafe_test.go

  1. goos: linux
  2. goarch: amd64
  3. Benchmark_Unsafe_IntIntMap_Set-4 10000000 318 ns/op 62 B/op 0 allocs/op
  4. Benchmark_Unsafe_IntAnyMap_Set-4 5000000 282 ns/op 57 B/op 1 allocs/op
  5. Benchmark_Unsafe_IntStrMap_Set-4 5000000 332 ns/op 82 B/op 1 allocs/op
  6. Benchmark_Unsafe_AnyAnyMap_Set-4 3000000 471 ns/op 73 B/op 2 allocs/op
  7. Benchmark_Unsafe_StrIntMap_Set-4 5000000 429 ns/op 82 B/op 1 allocs/op
  8. Benchmark_Unsafe_StrAnyMap_Set-4 3000000 424 ns/op 73 B/op 2 allocs/op
  9. Benchmark_Unsafe_StrStrMap_Set-4 2000000 515 ns/op 96 B/op 2 allocs/op
  10. Benchmark_Unsafe_IntIntMap_Get-4 10000000 133 ns/op 0 B/op 0 allocs/op
  11. Benchmark_Unsafe_IntAnyMap_Get-4 20000000 134 ns/op 0 B/op 0 allocs/op
  12. Benchmark_Unsafe_IntStrMap_Get-4 10000000 126 ns/op 0 B/op 0 allocs/op
  13. Benchmark_Unsafe_AnyAnyMap_Get-4 10000000 166 ns/op 0 B/op 0 allocs/op
  14. Benchmark_Unsafe_StrIntMap_Get-4 5000000 246 ns/op 7 B/op 0 allocs/op
  15. Benchmark_Unsafe_StrAnyMap_Get-4 10000000 238 ns/op 7 B/op 0 allocs/op
  16. Benchmark_Unsafe_StrStrMap_Get-4 5000000 229 ns/op 7 B/op 0 allocs/op

不同类型map性能

https://github.com/gogf/gf/blob/master/container/gmap/gmap_z_bench_maps_test.go

  1. goos: linux
  2. goarch: amd64
  3. Benchmark_HashMap_Set-4 5000000 349 ns/op 40 B/op 2 allocs/op
  4. Benchmark_ListMap_Set-4 3000000 455 ns/op 87 B/op 3 allocs/op
  5. Benchmark_TreeMap_Set-4 3000000 481 ns/op 28 B/op 2 allocs/op
  6. Benchmark_HashMap_Get-4 30000000 67.8 ns/op 0 B/op 0 allocs/op
  7. Benchmark_ListMap_Get-4 20000000 74.5 ns/op 0 B/op 0 allocs/op
  8. Benchmark_TreeMap_Get-4 20000000 189 ns/op 8 B/op 1 allocs/op

gmapsync.Map性能比较

go语言从1.9版本开始引入了并发安全的sync.Map,但gmap比较于标准库的sync.Map性能更加优异,并且功能更加丰富。

我们来看看基准测试对比结果:https://github.com/gogf/gf/blob/master/container/gmap/gmap_z_bench_syncmap_test.go

  1. goos: linux
  2. goarch: amd64
  3. Benchmark_GMapSet-4 10000000 209 ns/op 15 B/op 0 allocs/op
  4. Benchmark_SyncMapSet-4 3000000 451 ns/op 67 B/op 3 allocs/op
  5. Benchmark_GMapGet-4 30000000 66.4 ns/op 0 B/op 0 allocs/op
  6. Benchmark_SyncMapGet-4 30000000 36.0 ns/op 0 B/op 0 allocs/op
  7. Benchmark_GMapRemove-4 10000000 207 ns/op 0 B/op 0 allocs/op
  8. Benchmark_SyncMapRmove-4 30000000 42.4 ns/op 0 B/op 0 allocs/op