首頁 > 軟體

GoFrame gmap遍歷hashmap listmap treemap使用技巧

2022-06-10 14:02:01

文章比較硬核,爆肝2千多字,除了hashmap、listmap、treemap使用技巧閱讀還有使用gmap的踩坑之旅,閱讀大約需要5~10分鐘。

先說結論

map型別

一圖勝千言:

範例化範例:

   hashMap := gmap.New(true)
   listMap := gmap.NewListMap(true)
   treeMap := gmap.NewTreeMap(gutil.ComparatorInt, true)

使用技巧

當我們對返回順序有要求時不能使用hashmap,因為hashmap返回的是無序列表;

當需要按輸入順序返回結果時使用listmap;

當需要讓返回結果自然升序排列時使用treemap

package main
import (
   "fmt"
   "github.com/gogf/gf/container/gmap"
   "github.com/gogf/gf/frame/g"
   "github.com/gogf/gf/util/gutil"
)
func main() {
   array := g.Slice{5, 1, 2, 7, 3, 9, 0}
   hashMap := gmap.New(true)
   listMap := gmap.NewListMap(true)
   treeMap := gmap.NewTreeMap(gutil.ComparatorInt, true)
   // 賦值
   for _, v := range array {
      hashMap.Set(v, v)
      listMap.Set(v, v)
      treeMap.Set(v, v)
   }
   //列印結果
   fmt.Println("hashMap.Keys()  :", hashMap.Keys())
   fmt.Println("hashMap.Values():", hashMap.Values())
   //從列印結果可知hashmap的鍵列表和值列表返回值的順序沒有規律,隨機返回
   fmt.Println("listMap.Keys()  :", listMap.Keys())
   fmt.Println("listMap.Values():", listMap.Values())
   //listmap鍵列表和值列表有序返回,且順序和寫入順序一致
   fmt.Println("treeMap.Keys()  :", treeMap.Keys())
   fmt.Println("treeMap.Values():", treeMap.Values())
   //treemap鍵列表和值列表也有序返回,但是不和寫入順序一致,按自然數升序返回
}

列印結果

hashMap.Keys()  : [5 1 2 7 3 9 0]
hashMap.Values(): [2 7 3 9 0 5 1]
listMap.Keys()  : [5 1 2 7 3 9 0]
listMap.Values(): [5 1 2 7 3 9 0]
treeMap.Keys()  : [0 1 2 3 5 7 9]
treeMap.Values(): [0 1 2 3 5 7 9]

為了讓大家更好的理解gmap,下面介紹一下gmap的基礎使用和一些進階技巧。

基礎概念

GoFrame框架(下文簡稱gf)提供的資料型別,比如:字典gmap、陣列garray、集合gset、佇列gqueue、樹形結構gtree、連結串列glist都是支援設定並行安全開關的。

支援設定並行安全開關這也是gf提供的常用資料型別和原生資料型別非常重要的區別

今天和大家分享gf框架中gmap相關知識點

對比sync.Map

go語言提供的原生map不是並行安全的map型別

go語言從1.9版本開始引入了並行安全的sync.Map,但gmap比較於標準庫的sync.Map效能更加優異,並且功能更加豐富。

基礎使用

  • gmap.New(true) 在初始化的時候開啟並行安全開關
  • 通過 Set() 方法賦值,通過 Sets() 方法批次賦值
  • 通過 Size() 方法獲取map大小
  • 通過 Get() 根據key獲取value值
  • ...

為了方便大家更好的檢視效果,在下方程式碼段中標明瞭列印結果

package main
import (
   "fmt"
   "github.com/gogf/gf/container/gmap"
)
func main() {
   m := gmap.New(true)
   // 設定鍵值對
   for i := 0; i < 10; i++ {
      m.Set(i, i)
   }
   fmt.Println("查詢map大小:", m.Size())
   //批次設定鍵值對
   m.Sets(map[interface{}]interface{}{
      10: 10,
      11: 11,
   })
   // 目前map的值
   fmt.Println("目前map的值:", m)
   fmt.Println("查詢是否存在鍵值對:", m.Contains(1))
   fmt.Println("根據key獲得value:", m.Get(1))
   fmt.Println("刪除資料", m.Remove(1))
   //刪除多組資料
   fmt.Println("刪除前的map大小:", m.Size())
   m.Removes([]interface{}{2, 3})
   fmt.Println("刪除後的map大小:", m.Size())
   //當前鍵名列表
   fmt.Println("鍵名列表:", m.Keys())   //我們發現是無序列表
   fmt.Println("鍵值列表:", m.Values()) //我們發現也是無序列表
   //查詢鍵名,當鍵值不存在時寫入預設值
   fmt.Println(m.GetOrSet(20, 20))   //返回值是20
   fmt.Println(m.GetOrSet(20, "二十")) //返回值仍然是20,因為key對應的值存在
   m.Remove(20)
   fmt.Println(m.GetOrSet(20, "二十")) //返回值是二十,因為key對應的值不存在
   // 遍歷map
   m.Iterator(func(k interface{}, v interface{}) bool {
      fmt.Printf("%v:%v n", k, v)
      return true
   })
   //自定義寫鎖操作
   m.LockFunc(func(m map[interface{}]interface{}) {
      m[88] = 88
   })
   // 自定義讀鎖操作
   m.RLockFunc(func(m map[interface{}]interface{}) {
      fmt.Println("m[88]:", m[88])
   })
   // 清空map
   m.Clear()
   //判斷map是否為空
   fmt.Println("m.IsEmpty():", m.IsEmpty())
}

執行結果

上面介紹的基礎使用比較簡單,下面介紹進階使用。

合併 merge

注意:Merge()的引數需要是map的參照型別,也就是傳map的取址符。

package main
import (
   "fmt"
   "github.com/gogf/gf/container/gmap"
)
func main() {
   var m1, m2 gmap.Map
   m1.Set("k1", "v1")
   m2.Set("k2", "v2")
   m1.Merge(&m2)
   fmt.Println("m1.Map()", m1.Map()) //m1.Map() map[k1:v1 k2:v2]
   fmt.Println("m2.Map()", m2.Map()) //m2.Map() map[k2:v2]
}

序列化

正如上一篇 GoFrame glist 基礎使用和自定義遍歷 介紹的,gf框架提供的資料型別不僅支援設定並行安全,也都支援序列化和反序列化。

json序列化和反序列化:序列化就是轉成json格式,反序列化就是json轉成其他格式型別(比如:map、陣列、物件等)

package main
import (
   "encoding/json"
   "fmt"
   "github.com/gogf/gf/container/gmap"
)
func main() {
   // 序列化
   //var m gmap.Map
   m := gmap.New() //必須範例化 只是像上面宣告但是不進行範例化,是無法序列化成功的
   m.Sets(map[interface{}]interface{}{
      "name": "王中陽",
      "age":  28,
   })
   res, _ := json.Marshal(m)
   fmt.Println("序列化結果:", res) //列印結果:{"age":28,"name":"王中陽"}
   // 反序列化
   m2 := gmap.New()
   s := []byte(`{"age":28,"name":"王中陽"}`)
   _ = json.Unmarshal(s, &m2)
   fmt.Println("反序列化結果:", m2.Map()) //反序列化結果: map[age:28 name:王中陽]
}

踩坑

正如上面程式碼段中註釋提到的:

在進行序列化操作時,必須範例化map

m := gmap.New() 

只是宣告map而不進行範例化,是無法序列化成功的

var m gmap.Map

過濾空值

package main
import (
   "fmt"
   "github.com/gogf/gf/container/gmap"
)
func main() {
   //首先明確:空值和nil是不一樣的,nil是未定義;而空值包括空字串,false、0等
   m1 := gmap.NewFrom(map[interface{}]interface{}{
      "k1": "",
      "k2": nil,
      "k3": 0,
      "k4": false,
      "k5": 1,
   })
   m2 := gmap.NewFrom(map[interface{}]interface{}{
      "k1": "",
      "k2": nil,
      "k3": 0,
      "k4": false,
      "k5": 1,
   })
   m1.FilterEmpty()
   m2.FilterNil()
   fmt.Println("m1.FilterEmpty():", m1) //預測結果: k5:1
   fmt.Println("m2.FilterNil():", m2)   //預測結果:除了k2,其他都返回
   // 列印結果和預期的一致:
   //m1.FilterEmpty(): {"k5":1}
   //m2.FilterNil(): {"k1":"","k3":0,"k4":false,"k5":1}
}

列印結果

m1.FilterEmpty(): {"k5":1}
m2.FilterNil(): {"k1":"","k3":0,"k4":false,"k5":1}

鍵值對反轉 Flip

package main
import (
   "github.com/gogf/gf/container/gmap"
   "github.com/gogf/gf/frame/g"
)
func main() {
   // 鍵值對反轉flip
   var m gmap.Map
   m.Sets(map[interface{}]interface{}{
      "k1": "v1",
      "k2": "v2",
   })
   fmt.Println("反轉前:", m.Map())
   m.Flip()
   fmt.Println("反轉後:", m.Map())
}

列印結果

反轉前:{
       "k1": "v1",
       "k2": "v2"
}
反轉後:{
       "v1": "k1",
       "v2": "k2"
}   

出棧(隨機出棧)

package main
import (
   "fmt"
   "github.com/gogf/gf/container/gmap"
)
func main() {
   //pop pops map出棧(彈棧)
   var m gmap.Map
   m.Sets(map[interface{}]interface{}{
      1: 1,
      2: 2,
      3: 3,
      4: 4,
      5: 5,
   })
   fmt.Println("m.Pop()之前:", m.Map())
   key, value := m.Pop()
   fmt.Println("key:", key)
   fmt.Println("value:", value)
   fmt.Println("m.Pop()之後:", m.Map()) //多次測試後發現是隨機出棧,不能理所當然的認為按順序出棧
   res := m.Pops(2) //引數是出棧個數
   fmt.Println("res:", res)
   fmt.Println("m.Pops之後:", m.Map()) //多次測試之後發現也是隨機出棧
}

執行結果

踩坑

注意:多次測試後發現是隨機出棧,不能理所當然的認為按順序出棧

總結

通過這篇文章,我們瞭解到:

重點消化一下map遍歷時,不同map的特點:

  • 1.1 當我們對返回順序有要求時不能使用hashmap,因為hashmap返回的是無序列表;
  • 1.2 當需要按輸入順序返回結果時使用listmap;
  • 1.3 當需要讓返回結果自然升序排列時使用treemap

gmap的基礎使用和進階使用技巧:反轉map、序列化、合併map、出棧等。

gf框架提供的資料結構,比如:字典gmap、陣列garray、集合gset、佇列gqueue、樹形結構gtree、連結串列glist 都是支援設定並行安全開關的;而且都支援序列化和反序列化,實現了標準庫json資料格式的序列化/反序列化介面。

以上就是GoFrame gmap遍歷hashmap listmap treemap使用技巧的詳細內容,更多關於GoFrame gmap遍歷的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com