如果有两个map,内容都一样,只有顺序不同

m1:=map[string]int{"a":,"b":,"c":};
m2:=map[string]int{"a":,"c":,"b":};

我们怎么判断二者是否一致呢?

如果你打算这么写:

fmt.Println("m1==m2",m1==m2)

这是行不通的,go没有重写map的==操作符,编译器会报告错误:

 invalid operation: m1 == m2 (map can only be compared to nil)

意思是map的变量只能和空(nil)比较,例如:

fmt.Println("m1 == nil?",m1==nil)
fmt.Println("m2 != nil?",m2!=nil)

这没有问题,执行结果是:

Running...

m1 == nil? false
m2 != nil? true

那怎么比较呢?如果要编程实现,还真是麻烦,比如我的想法是:循环m1,看看每个key是否都在m2中存在,再比较m1[key]是否和m2[key]相等,如果都ok,再依次循环m2。还真是麻烦:

func cmpMap(m1,m2  map[string]int)bool{
for k1,v1 :=range m1{
if v2,has:=m2[k1];has{
if v1!=v2 {
return false
}
}else{
return false;
}
}
for k2,v2:=range m2{
if v1,has:=m1[k2];has{
if v1!=v2{
return false;
}
}else{
return false;
}
}
return true;
}

其实,go的反射包中有一个巨好用的武器reflect.DeepEqual,可以方便解决这个问题,请看:

package main

import(
"fmt" "reflect"
)
type tt struct{
Code int
}
func main(){
m1:=map[string]int{"a":,"b":,"c":};
m2:=map[string]int{"a":,"c":,"b":}; fmt.Println("m1 == nil?",m1==nil)
fmt.Println("m2 != nil?",m2!=nil)
//fmt.Println("m1==m2",m1==m2)
fmt.Println("cmpMap(m1,m2) = ",cmpMap(m1,m2));
fmt.Println("reflect.DeepEqual(m1,m2) = ",reflect.DeepEqual(m1,m2))
fmt.Println()
m3:=map[string]int{"a":,"b":,"c":,"d":};
fmt.Println("cmpMap(m1,m3)=",cmpMap(m1,m3));
fmt.Println("reflect.DeepEqual(m1,m3) = ",reflect.DeepEqual(m1,m3))
}
func cmpMap(m1,m2 map[string]int)bool{
for k1,v1 :=range m1{
if v2,has:=m2[k1];has{
if v1!=v2 {
return false
}
}else{
return false;
}
}
for k2,v2:=range m2{
if v1,has:=m1[k2];has{
if v1!=v2{
return false;
}
}else{
return false;
}
}
return true;
}

执行结果:

Running...

m1 == nil? false
m2 != nil? true
cmpMap(m1,m2) = true
reflect.DeepEqual(m1,m2) = true cmpMap(m1,m3)= false
reflect.DeepEqual(m1,m3) = false Success: process exited with code .

但是美中不足的是,由于reflect.DeepEqual需要经过反射操作,效率比我们自己写的函数差的多了,写个简单的测试:

        start:=time.Now();
for i:=0;i<100000;i++{
cmpMap(m1,m2)
}
end:=time.Now();
du:=end.Sub(start)
fmt.Println("100000 call cmpMap(m1,m2) elapsed=",du) start=time.Now();
for i:=0;i<100000;i++{
reflect.DeepEqual(m1,m2);
}
end=time.Now();
du=end.Sub(start);
fmt.Println("100000 call reflect.DeepEqual(m1,m2) elapsed=",du)

看看结果,大约有10倍的差距

 call cmpMap(m1,m2) elapsed= .544992ms
call reflect.DeepEqual(m1,m2) elapsed= .577069ms

当然,在一般情况下,这点儿性能损失不算什么,尤其在不确定类型需要反射的时候,更是我们不可不用的强大工具。

比如:

func main(){
m1:=map[string]interface{}{"a":"", "b":, "c":};
m2:=map[string]interface{}{"a":, "c":"", "b":}; fmt.Println(`reflect.DeepEqual(m1["a"],m2["a"]`,reflect.DeepEqual(m1["a"],m2["a"]));
fmt.Println(`reflect.DeepEqual(m1["b"],m2["b"]`,reflect.DeepEqual(m1["b"],m2["b"]));
}

执行结果:

Running...

reflect.DeepEqual(m1["a"],m2["a"] false
reflect.DeepEqual(m1["b"],m2["b"] true

这种情况,如果我们自己写代码比较,势必要使用switch type语法,实在是太麻烦了,感谢go包含了这么好的工具。

【玩转Golang】reflect.DeepEqual的更多相关文章

  1. 结构体深度比较 reflect.DeepEqual

    demo1 package main import ( "fmt" "reflect" ) func main() { sliceMap1 := make([] ...

  2. golang reflect

    golang reflect go语言中reflect反射机制.详细原文:地址 接口值到反射对象 package main import ( "fmt" "reflect ...

  3. golang reflect包使用解析

    golang reflect包使用解析 参考 Go反射编码 2个重要的类型 Type Value 其中Type是interface类型,Value是struct类型,意识到这一点很重要 Type和Va ...

  4. golang reflect知识集锦

    目录 反射之结构体tag Types vs Kinds reflect.Type vs reflect.Value 2019/4/20 补充 reflect.Value转原始类型 获取类型底层类型 遍 ...

  5. golang reflect 简单使用举例

    golang中的多态,主要由接口interface体现. 接口interface在实现上,包括两部分:动态类型和动态值. golang提供的reflect包可以用来查看这两部分. 动态类型 func ...

  6. 【玩转Golang】 通过组合嵌入实现代码复用

    应用开发中的一个常见情景,为了避免简单重复,需要在基类中实现共用代码,着同样有助于后期维护. 如果在以往的支持类继承的语言中,比如c++,Java,c#等,这很简单!可是go不支持继承,只能mixin ...

  7. Golang reflect 反射

    反射的规则如下: 从接口值到反射对象的反射  从反射对象到接口值的反射  为了修改反射对象,其值必须可设置   -------------------------------------------- ...

  8. 【玩转Golang】beego下实现martini中的透明式静态文件服务(static folder)效果。

    出于效率等原因,最近将web框架由martini切换为了beego,其他地方都很平顺,只是两个框架的handler签名不一致,需要修改,所以耗时较长,这是预计到的.但是有一个地方没有预计到,也耗费了较 ...

  9. 【玩转Golang】slice切片的操作——切片的追加、删除、插入等

    一.一般操作 1,声明变量,go自动初始化为nil,长度:0,地址:0,nil func main(){ var ss []string; fmt.Printf("length:%v \ta ...

随机推荐

  1. ython strip lstrip rstrip使用方法

    Python中的strip用于去除字符串的首尾字符,同理,lstrip用于去除左边的字符,rstrip用于去除右边的字符. 这三个函数都可传入一个参数,指定要去除的首尾字符. 需要注意的是,传入的是一 ...

  2. idea properties文件unicode码问题

    在git hub上下载了个工程.但是properties文件一直显示不了中文: # \u662F\u5426\u4F7F\u7528\u8FDC\u7A0B\u914D\u7F6E\u6587\u4E ...

  3. UK 更新惊魂记

    本文前提是.由于更easy安装各种webserver.数据库,redis缓存.mq等软件,笔者使用Ubuntu Kylin作为开发系统已经好长时间了. 而今天(2015-07-23)下午2时许,系统提 ...

  4. 阿里云扩容数据盘_Linux

    扩容数据盘_Linux 更新时间:2018-10-11 15:18:46 · 编辑者 编辑 · ★ 我的收藏 新手学堂 学习路径 本页目录 前提条件 注意事项 步骤 1. 在控制台上扩容数据盘的磁盘空 ...

  5. WebUpload formdata 上传参数

    https://www.cnblogs.com/wisdo/p/6159761.html webUploader 是款很好用的优秀的开源上传组件,由百度公司开发,详细的介绍可参见webUploader ...

  6. css3学习系列之初识 transform (一)

    一.transform是哈? 从字面意思来看 transform的含义是:改变,使…变形:转换,对 没错 就是变形 变形当有 放大缩小 东倒 西歪,刚好css3中 transform 也出这个 放几个 ...

  7. Qt 菜鸟的坑 QAbstractSocket::isValid()

    我曾经多次在 Qt socket 编程中使用 tcpSocket.isValid 来判断我当前的连接是否可用,最近写程序时才发现此法并不妥当. bool QAbstractSocket::isVali ...

  8. so在genymotation中错误问题

    genymotation的android模拟器运行起来非常快,但是有些项目安装到上面不是crash,log一般是so文件调用失败的信息,或则直接提示INSTALL_FAILED_CPU_ABI_INC ...

  9. Python之cv2

    1.读取图片 import cv2 img = cv2.imread('./test.jpg') 读取出来的图片是numpy.ndarray格式,值是0-255, img的形状是 (图片高度,图片宽度 ...

  10. Union和Union All的区别[转]

    来源:http://blog.csdn.net/wanghai__/article/details/4712555/ 假设我们有一个表Student,包括以下字段与数据: drop table stu ...