本文主要是来聊一聊关于 Golang 中的深度比较 DeepEqual
因为最近发现身边的小伙伴写 2 个或者多个 map 比较的时候,都是自己去实现去比较每一个结构,每一个节点的 key 和 value 是不是都相等,且根据不同的数据结构,都要去实现一遍,没有必要自己造轮子
==
==
==
此处你是否可能会想到 C++ 还可以去重载操作符,咱们实现一下对应数据类型的操作符就可以了,妥妥的
===
那么,看到此处,是否可以猜测 golang 的做法也是类似的呢?
实际上 golang 去比较两个对象是否相同,也是通过去比较数据的类型,数据的值,数据的长度等等维度来进行确认的
===
DeepEqual 案例
funcDeepEqual(x, y interface{}) bool
很多朋友在不知道 golang 有提供 DeepEqual 功能的时候,比较 2 个 map 可能会这样去实现:
funcmapEqual(m1, m2 map[string]int) bool{ fork, v := rangem1 { vv, ok := m2[k] if!ok { returnfalse } ifv != vv { returnfalse } } returntrue}
当然也没有啥问题,但是如果这个时候需要我们比较两个切片是否相等,两个结构体是否相等,甚至两个 interface{} 是否相等的时候,是不是都要去写对应的工具函数呢?
使用 DeepEqual 比较 map
两个同一类型的 map,使用自己编写的 mapEqual 和 使用 DeepEqual 我们得到的结果都是我们所期望的
但是对于 DeepEqual 来说,你可以传入任何类型的数据,入参是 2 个 interface{} 类型的数据,响应是 bool
对于 mapEqual 来说,你就只能传入 map[string]int 类型的数据,看到此处,自己造轮子,弊端还是很明显的吧
自然,你也可以去将参数设计成 interface{} 类型的,然后再去进行各种反射处理
可是明明有官方库,何必自己再弄一遍呢,我们不应该是吸收官方的思想和精华,站在巨佬的肩膀上去做更多有意义的事情吗
使用 DeepEqual 比较 map 和 自定义类型
可使用 DeepEqual 的时候,一定要知道他的运作机制和原理
例如下面的案例,我们自定义一个数据类型 myType,实际上和 map[string]int 是一样的, 可是我们去将原生的 map[string]int 的 m1 和 myType 类型 m2 进行比较的时候,他们实际上是不相等的
那么,看到这里,实际上 DeepEqual 自然是要比我们自己写的 mapEqual 强太多了,他不仅关注数据的值,还会关注具体数据的类型,根据不同的数据类型,来进行不同的数据校验和比较
DeepEqual 原理
==
如果是其他的数据类型,那么就会递归的去调用 deepValueEqual 来一层一层的去校验数据和比较
为什么需要递归调用呢?
==
那么切片中也是可以是其他的任意数据结构的,也可以是自定义的结构体
因此在做这种比较的时候,遍历到切片元素的时候,也要去确认元素是什么类型的,如果是上述提到的非简单类型,那么仍然需要继续一层一层的识别他的类型,和他的值,再进行逐个比较
例如这样去比较这样结构的切片,真的完全有必要递归去一层一层的确认数据类型和数值typeNode struct{ M map[string]int In interface{} Sli []map[int][chanint]}varsli = []Node{...}
在比较的过程中,哪怕有一个环节不是我们所期望的,那么都会直接返回 false,即不相等
那么,仔细看 DeepEqual 的注释,我们可以看到,这里有详细的关于各种数据类型的比较和校验细节,翻译一下仅供大家参考,希望你有机会用到
- 数组 Array
比较相同索引处的元素是否相等
- 结构体 struct
比较相应字段,包括导出和不导出(此处表示字段开头是大写还是小写)
- 函数 Func
只有当函数为 nil 的时候,才会是相等的,其他情况都不相等
- Interface{}
两者都存在具体的值的时候,那么是相等的
- Map
都为空的时候是可以是相等的
都不为空的时候,会去比较他们的长度,他们是否有相同的 key 且对应相同的 value ,若都相同,则相等
- 指针 pointer
可以直接使用 == 进行比较,和 == 效果一致,或者指针指向的值是相等的
- 切片 slice
都为 nil 的时候,是相等的
不为空的时候,会去比较他们的长度,且指向的底层数组也得有相同的元素,也就是指向底层数组的地址是相同的
- 对于 other values 其他的数据类型,例如整型,布尔,字符串,通道
====
总结
本次主要聊了关于
- 非简单数据类型的比较如何去处理
- DeepEqual 的使用方式以及注意事项
- DeepEqual 的原理和其支持的数据类型的判定规则