GoFrame的gmap相比Go原生的map,天然支持排序和有序遍历!?

article/2025/11/11 7:32:18

大家好,我是阳哥。内容比较硬核,建议先收藏再观看。

我也在B站发布了这期内容的视频版,视频相比文章看起来确实更通俗易懂。
如果你是初学者建议先看视频:欢迎大家点击这个链接观看。
觉得不错,欢迎关注、三连一波。谢谢!

如果你有经验,想节省时间,请直接阅读文章:

前言

有好多初学GO和GoFrame的小伙伴搞不清楚map怎么用。

不少刚入门的小伙伴都被Go语言中map的无序性“坑过”,尤其是PHP转Go的小伙伴,毕竟用惯了PHP的数组。

这篇文章就是给初学的小伙伴们答疑解惑的,会为大家介绍:

为什么Go语言中的map是无序的,如何自定义实现map的排序?

(Ps:这部分不作为这篇文章的重点,感兴趣的小伙伴可以看我之前整理的这篇文章:# Go容易搞错的知识点汇总:Go map如何实现排序 部分)

GoFrame的gmap相比于Go原生的map有什么优势?为什么天然支持排序和有序遍历!?

先说结论

GoFrame提供的gmap字典类型,包含多个数据结构的map容器:HashMapTreeMapListMap。其中TreeMap支持排序,TreeMapListMap支持有序遍历。

使用技巧

我们在使用GoFrame的gmap时,要结合自己的场景使用合适的map容器:

  1. 当我们对返回顺序有要求时不能使用HashMap,因为HashMap返回的是无序列表;
  2. 当需要按输入顺序返回结果时使用ListMap
  3. 当需要让返回结果按照自然升序排列时使用TreeMap

注意:gmap的实例化默认是HashMap类型:hashMap := gmap.New(true)

一图胜千言

GoFrame gmap 基本介绍:

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

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

在这里插入图片描述

实例化示例:

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

实践得真知

package mainimport ("fmt""github.com/gogf/gf/v2/container/gmap""github.com/gogf/gf/v2/frame/g""github.com/gogf/gf/v2/util/gutil"
)func main() {array := g.Slice{1, 5, 2, 3, 4, 6, 8, 7, 9}hashMap := gmap.New(true)listMap := gmap.NewListMap(true)treeMap := gmap.NewTreeMap(gutil.ComparatorInt, true)for _, v := range array {hashMap.Set(v, v)}for _, v := range array {listMap.Set(v, v)}for _, v := range array {treeMap.Set(v, v)}fmt.Println("HashMap   Keys:", hashMap.Keys())   //HashMap   Keys: [7 9 1 5 2 4 6 3 8]fmt.Println("HashMap Values:", hashMap.Values()) //HashMap Values: [6 7 9 1 5 2 4 3 8]//从打印结果可知hashmap的键列表和值列表返回值的顺序没有规律,随机返回fmt.Println("ListMap   Keys:", listMap.Keys())   //ListMap   Keys: [1 5 2 3 4 6 8 7 9]fmt.Println("ListMap Values:", listMap.Values()) //ListMap Values: [1 5 2 3 4 6 8 7 9]//listmap键列表和值列表有序返回,且顺序和写入顺序一致fmt.Println("TreeMap   Keys:", treeMap.Keys())   //TreeMap   Keys: [1 2 3 4 5 6 7 8 9]fmt.Println("TreeMap Values:", treeMap.Values()) //TreeMap Values: [1 2 3 4 5 6 7 8 9]//treemap键列表和值列表也有序返回,但是不和写入顺序一致,按自然数升序返回
}

打印结果

通过打印结果我们可以发现:

  1. hashmap的键列表和值列表返回值的顺序没有规律,随机返回

  2. listmap键列表和值列表有序返回,且顺序和写入顺序一致

  3. treemap键列表和值列表也有序返回,但是不和写入顺序一致,按自然数升序返回

这也佐证了我开篇提到的使用技巧。

在这里插入图片描述

为了让大家更好的理解gmap,下面介绍一下gmap的基础使用和一些进阶技巧。

基础概念

GoFrame框架(下文简称gf)提供的数据类型,比如:字典gmap、数组garray、集合gset、队列gqueue、树形结构gtree、链表glist都是支持设置并发安全开关的。

支持设置并发安全开关这也是gf提供的常用数据类型和原生数据类型重要的区别之一。

对比sync.Map

Go语言提供的原生map不是并发安全的map类型

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

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

对性能测试感兴趣的小伙伴可以详细看下官方文档的介绍,不作为这篇文章的重点。

基础使用

  1. gmap.New(true) 在初始化的时候开启并发安全开关
  2. 通过 Set() 方法赋值,通过 Sets() 方法批量赋值
  3. 通过 Size() 方法获取map大小
  4. 通过 Get() 根据key获取value值

更多操作大家可以直接查看下方的代码示例,也欢迎大家动手复刻

为了方便大家更好的查看效果,在下方代码段中标明了打印结果:

package mainimport ("fmt""github.com/gogf/gf/v2/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))   //返回值是20fmt.Println(m.GetOrSet(20, "二十")) //返回值仍然是20,因为key对应的值存在m.Remove(20)fmt.Println(m.GetOrSet(20, "二十")) //返回值是二十,因为key对应的值不存在// 遍历mapm.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])})// 清空mapm.Clear()//判断map是否为空fmt.Println("m.IsEmpty():", m.IsEmpty())
}

运行结果

在这里插入图片描述

上面介绍的基础使用比较简单,下面介绍进阶使用。

合并 merge

注意:Merge()的参数需要是map的引用类型,也就是参数需要传map的取址符。

package mainimport ("fmt""github.com/gogf/gf/v2/container/gmap"
)func main() {var m1, m2 gmap.Mapm1.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 mainimport ("encoding/json""fmt""github.com/gogf/gf/v2/container/gmap"
)func main() {// 序列化//var m gmap.Mapm := gmap.New() //必须实例化 只是像上面声明但是不进行实例化,是无法序列化成功的m.Sets(map[interface{}]interface{}{"name": "王中阳","age":  28,})res, _ := json.Marshal(m)fmt.Println("序列化结果:", string(res)) //打印结果:{"age":28,"name":"王中阳"}// 反序列化m2 := gmap.New()s := []byte(`{"age":28,"name":"王中阳"}`)_ = json.Unmarshal(s, &m2)fmt.Println("反序列化结果:", m2.Map()) //反序列化结果: map[age:28 name:王中阳]
}

打印结果

在这里插入图片描述

踩坑

正如上面代码段中注释掉的://var m gmap.Map

在进行序列化操作时,必须实例化map

m := gmap.New() 

在这里插入图片描述

只是声明map而不进行实例化,是无法序列化成功的

var m gmap.Map

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xLCfmugd-1667438337205)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fe935778374e4169a02c3c881ac17a9f~tplv-k3u1fbpfcp-watermark.image?)]

另外一个需要注意的知识点就是过滤空值了:

过滤空值

package mainimport ("fmt""github.com/gogf/gf/v2/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:1fmt.Println("m2.FilterNil():", m2)   //预测结果:除了k2,其他都返回// 打印结果和预期的一致://m1.FilterEmpty(): {"k5":1}//m2.FilterNil(): {"k1":"","k3":0,"k4":false,"k5":1}
}

打印结果

在这里插入图片描述

还有一个非常好用的特性,键值对反转:

键值对反转 Flip

package mainimport ("github.com/gogf/gf/v2/container/gmap""github.com/gogf/gf/v2/frame/g"
)func main() {// 键值对反转flipvar m gmap.Mapm.Sets(map[interface{}]interface{}{"k1": "v1","k2": "v2",})fmt.Println("反转前:", m.Map())m.Flip()fmt.Println("反转后:", m.Map())
}

打印结果

在这里插入图片描述

出栈(随机出栈)

这个出栈的知识点和我开篇的使用技巧呼应上了:

package mainimport ("fmt""github.com/gogf/gf/v2/container/gmap"
)func main() {//pop pops map出栈(弹栈)var m gmap.Mapm.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()) //多次测试之后发现也是随机出栈
}

运行结果

在这里插入图片描述

踩坑

注意:多次测试后发现是随机出栈,不能理所当然的认为按顺序出栈。

我们深入思考一下原因:其实很简单,因为gmap的底层实现是hashmap,本身就是无序的,当然不可能按顺序出栈了。

总结

好了,我们再来回顾一下这篇文章的重点:

  1. 我们在使用GoFrame的gmap时,要结合自己的场景使用合适的map容器:
    1. 当我们对返回顺序有要求时不能使用HashMap,因为HashMap返回的是无序列表;
    2. 当需要按输入顺序返回结果时使用ListMap
    3. 当需要让返回结果按照自然升序排列时使用TreeMap
    4. gmap的实例化默认是HashMap类型:hashMap := gmap.New(true)
  2. gmap的基础使用和进阶使用技巧:反转map、序列化、合并map、出栈等。
  3. gf框架提供的数据结构,比如:字典gmap、数组garray、集合gset、队列gqueue、树形结构gtree、链表glist 都是支持设置并发安全开关的;而且都支持序列化和反序列化,实现了标准库json数据格式的序列化/反序列化接口。

一起学习


http://chatgpt.dhexx.cn/article/vDQ0ZE8p.shtml

相关文章

Gmap安装使用

最近使用c#做了一个地面站&#xff0c;需要用到地面站&#xff0c;在论文里看到好多人都是用的Gmap&#xff0c;所以今天写个博客&#xff0c;记录一下自己遇到的问题。 1.下载 下载地址&#xff1a;https://archive.codeplex.com/?pgreatmaps 2.编译 大家都看到&#xff…

WPF GMap使用高德地图

文章目录 前言一、Nuget下载Gmap二、代码1.添加类2.加载高德地图 总结 前言 近日在项目中用到了GMap&#xff0c;并且Gmap中使用了高德地图&#xff0c;特此记录一下。 一、Nuget下载Gmap 在Nuget中搜索GMap&#xff0c;选择GMap.NET.Presentatiom进行下载安装。&#xff08;…

GMap.NET使用教程

原文地址&#xff1a;GMap.NET使用教程 GMap.NET是一个强大、免费、跨平台、开源的.NET控件&#xff0c;它在Windows Forms和WPF环境中能够通过Google, Yahoo!, Bing, OpenStreetMap, ArcGIS, Pergo, SigPac等实现路径规划、地理编码以及地图展示功能&#xff0c;并支持缓存和运…

基于GMap.NET库实现的Windows桌面地图工具软件分享

0 前言 由于工作中经常和地图、GPS坐标转换、GPS轨迹查看等内容相关&#xff0c;经常要借助一些在线网站和工具来查看地图和位置等&#xff0c;在一次偶然的机会中了解到一个GMap.NET开源的桌面地图库和基于GMap.NET实现的MapDownloader地图下载工具&#xff0c;于是也想实现一…

GMap.NET入门详细教程【4】--------为控件添加事件,在鼠标单击时打点

GMap.NET入门 下载 GMap.NET&#xff0c;并在VS中添加GMap.NET控件初始化并加载一张地图添加标记点、线、多边形为控件添加事件&#xff0c;在鼠标单击时打点 快捷添加 选中窗体中的GMap控件&#xff0c;并查看内置事件 在这里&#xff0c;通过使用MouseDown和DoubleClick事…

GMap.net控件学习记录

主要参考网址 http://www.cnblogs.com/luxiaoxun/p/3802559.html http://www.cnblogs.com/luxiaoxun/p/3463250.html http://blog.csdn.net/onepiecehuiyu/article/details/19159565 GMap官方网址 http://greatmaps.codeplex.com/ WGS84&#xff0c;GCJ02&#xff0c;BD09坐标转…

Gmap使用说明,通过输入经纬度查询位置

由于本人对于Gmap的使用时间不长&#xff0c;有很多东西不是太熟悉&#xff0c;所以本人的代码有借鉴的部分&#xff0c;如有发现侵权&#xff0c;还请及时联系本人。 我目前已经基本实现了&#xff0c;地图的放大、缩小、平移的功能。完成了鼠标单击标点&#xff0c;输入经纬度…

GMap.net 自定义Marker

说明 自定义Marker部分内容来源于互联网&#xff0c;具体来源不记得了&#xff0c;若有人发现此处没注明出处&#xff0c;请海涵&#xff01; nuget包 GMapMarkerArrow using GMap.NET.WindowsForms; using System; using System.Collections.Generic; using System.Linq; usin…

C#基于开源地图GMap的开发示例

一.介绍 本示例程序是我在做项目前的探索示例。示例中测试了一些简单的功能&#xff0c;满足了我项目中的基本功能。更进一步的开发&#xff0c;有待继续研究。 二.项目源代码地址 源代码下载地址 三.项目讲解 1.引用GMap的dll文件 利用GMap开发&#xff0c;需要用到两个…

GMAP一款比对工具用于ALLHiC构建等位基因表

在ALLHiC使用过程中需要构建Allele.ctg.table&#xff0c;用于过滤多倍体基因组中因等位序列相似引起的HiC噪音的必要输入。官网提供了两种办法&#xff0c;一种是blastn&#xff0c;需要对草图基因组进行注释&#xff0c;这个过程挺麻烦的&#xff0c;在最下边看到了也可以使用…

Gmap使用心得分享C#-winform-Gmap

目录 一、Gmap库引用 1.下载Gmap引用库 2.Visual Studio添加项目引用 &#xff08;1&#xff09;打开项目后点击项目后添加引用 ​ &#xff08;2&#xff09;浏览本地库 &#xff08;3&#xff09;添加引用即可 二、Gmap使用流程 1.添加GmapControl 2.图…

基于GMap.Net的地图解决方案

一 地图的加载与显示 关于GMap的介绍与使用可以看我以前的文章: GMap.Net开发之在WinForm和WPF中使用GMap.Net地图插件 GMap.Net是.Net下一个地图控件,可以基于Http协议加载各个公司的地图,如Google,Bing,ArcGIS的,主要原理是通过解析各个公司的地图服务的URL,传入相应的…

Jersey框架常用注解3:媒体类型注解@Consumes和@Produces

Consumes 指定http请求的MIME类型&#xff0c;默认是*/*&#xff0c;表示任意的MIME类型。该注解的值是数组类型&#xff0c;支持多个MIME类型&#xff0c;可以使用MediaType来指定MIME类型。 Produces 指定http响应的MIME类型&#xff0c;默认是*/*&#xff0c;表示任意的M…

12.@RequestMapping中的consumes属性和produces属性

请求头header中很重要的两个参数:Accept:text/html只在响应中存在,表示当前请求希望服务器将来返回的数据类型是text/htmlContent-Type:application/json既可以出现在请求中,也可以出现在响应中,例如响应中代表服务器响应的是什么数据类型响应中response.setContentType("…

springmvc @RequestMapping注解中produces以及consumes属性的含义

http协议基础知识 首先需要了解什么叫MediaType&#xff08;媒体类型&#xff09;&#xff1f; 通俗来说&#xff0c;在http协议中&#xff0c;用来表示传输内容的格式就是MediaType&#xff0c;比如text/html&#xff0c;application/json等&#xff0c;其中text代表介质&am…

AJAX、异步和同步区别

1. 概念&#xff1a; ASynchronous JavaScript And XML (异步的JavaScript 和 XML) 异步和同步区别 2.Ajax作用&#xff1a; Ajax 是一种在无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术。通过在后台与服务器进行少量数据交换&#xff0c;Ajax 可以使网页…

js异步与同步的区别

你应该知道&#xff0c;javascript语言是一门“单线程”的语言&#xff0c;不像java语言&#xff0c;类继承Thread再来个thread.start就可以开辟一个线程&#xff0c;所以&#xff0c;javascript就像一条流水线&#xff0c;仅仅是一条流水线而已&#xff0c;要么加工&#xff0…

同步调制和异步调制区别

在PWM控制电路中&#xff0c;载波频率 fc 和调制信号频率 fr 之比成为载波比&#xff0c;根据载波信号和信号波信号是否同步分为同步调制和异步调制。 1 什么是异步调制&#xff1f; 载波信号和调制信号不保持同步的方式称为异步调制&#xff0c;如下图所示&#xff0c;异步调…

同步和异步,区别

同步&#xff1a; 同步的思想是&#xff1a;所有的操作都做完&#xff0c;才返回给用户。这样用户在线等待的时间太长&#xff0c;给用户一种卡死了的感觉&#xff08;就是系统迁移中&#xff0c;点击了迁移&#xff0c;界面就不动了&#xff0c;但是程序还在执行&#xff0c;…

java同步和异步的区别_java中同步与异步区别是什么

一、同步与异步概念&#xff1a;(推荐&#xff1a;java视频教程) 1.同步&#xff1a;所有的操作都做完&#xff0c;才返回给用户。这样用户在线等待的时间太长&#xff0c;给用户一种卡死了的感觉(就是系统迁移中&#xff0c;点击了迁移&#xff0c;界面就不动了&#xff0c;但…