如何go语言比较两个对象是否深度相同

前言:

func DeepEqual(x, y interface{}) bool

DeepEqual 函数的参数是两个 interface,实际上也就是可以输入任意类型,输出 true 或者 flase 表示输入的两个变量是否是“深度”相等。

先明白一点,如果是不同的类型,即使是底层类型相同,相应的值也相同,那么两者也不是“深度”相等。

type MyInt int
type YourInt int
func main() {
    m := MyInt(1)
    y := YourInt(1)
    fmt.Println(reflect.DeepEqual(m, y)) // false
}

上面的代码中,m, y 底层都是 int,而且值都是 1,但是两者静态类型不同,前者是 MyInt,后者是 YourInt,因此两者不是“深度”相等。

在源码里,有对 DeepEqual 函数的非常清楚地注释,列举了不同类型,DeepEqual 的比较情形,这里做一个总结:

类型 深度相等情形
Array 相同索引处的元素“深度”相等
Struct 相应字段,包含导出和不导出,“深度”相等
Func 只有两者都是 nil 时
Interface 两者存储的具体值“深度”相等
Map 1、都为 nil;2、非空、长度相等,指向同一个 map 实体对象,或者相应的 key 指向的 value “深度”相等
Pointer 1、使用 == 比较的结果相等;2、指向的实体“深度”相等
Slice 1、都为 nil;2、非空、长度相等,首元素指向同一个底层数组的相同元素,即 &x[0] == &y[0] 或者 相同索引处的元素“深度”相等
numbers, bools, strings, and channels 使用 == 比较的结果为真

一般情况下,DeepEqual 的实现只需要递归地调用 == 就可以比较两个变量是否是真的“深度”相等。

但是,有一些异常情况:比如 func 类型是不可比较的类型,只有在两个 func 类型都是 nil 的情况下,才是“深度”相等;float 类型,由于精度的原因,也是不能使用 == 比较的;包含 func 类型或者 float 类型的 struct, interface, array 等。

对于指针而言,当两个值相等的指针就是“深度”相等,因为两者指向的内容是相等的,即使两者指向的是 func 类型或者 float 类型,这种情况下不关心指针所指向的内容。

同样,对于指向相同 slice, map 的两个变量也是“深度”相等的,不关心 slice, map 具体的内容。

对于“有环”的类型,比如循环链表,比较两者是否“深度”相等的过程中,需要对已比较的内容作一个标记,一旦发现两个指针之前比较过,立即停止比较,并判定二者是深度相等的。这样做的原因是,及时停止比较,避免陷入无限循环。

来看源码:

func DeepEqual(x, y interface{}) bool {
    if x == nil || y == nil {
        return x == y
    }
    v1 := ValueOf(x)
    v2 := ValueOf(y)
    if v1.Type() != v2.Type() {
        return false
    }
    return deepValueEqual(v1, v2, make(map[visit]bool), 0)
}

首先查看两者是否有一个是 nil 的情况,这种情况下,只有两者都是 nil,函数才会返回 true

接着,使用反射,获取x,y 的反射对象,并且立即比较两者的类型,根据前面的内容,这里实际上是动态类型,如果类型不同,直接返回 false。

最后,最核心的内容在子函数 deepValueEqual 中。

代码比较长,思路却比较简单清晰:核心是一个 switch 语句,识别输入参数的不同类型,分别递归调用 deepValueEqual 函数,一直递归到最基本的数据类型,比较 int,string 等可以直接得出 true 或者 false,再一层层地返回,最终得到“深度”相等的比较结果。

实际上,各种类型的比较套路比较相似,这里就直接节选一个稍微复杂一点的 map 类型的比较:

// deepValueEqual 函数
// ……
case Map:
    if v1.IsNil() != v2.IsNil() {
        return false
    }
    if v1.Len() != v2.Len() {
        return false
    }
    if v1.Pointer() == v2.Pointer() {
        return true
    }
    for _, k := range v1.MapKeys() {
        val1 := v1.MapIndex(k)
        val2 := v2.MapIndex(k)
        if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
            return false
        }
    }
    return true
// ……

和前文总结的表格里,比较 map 是否相等的思路比较一致,也不需要多说什么。说明一点,visited 是一个 map,记录递归过程中,比较过的“对”:

type visit struct {
    a1  unsafe.Pointer
    a2  unsafe.Pointer
    typ Type
}
map[visit]bool

比较过程中,一旦发现比较的“对”,已经在 map 里出现过的话,直接判定“深度”比较结果的是 true

到此这篇关于如何go语言比较两个对象是否深度相同的文章就介绍到这了,更多相关go比较对象内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • mongoose更新对象的两种方法示例比较

    前言 Mongoose是在node.js异步环境下对mongodb进行便捷操作的对象模型工具 那么要使用它,首先你得装上node.js和mongodb,关于mongodb的安装和操作介绍可以参考:http://www.jb51.net/article/80296.htm 演示 描述场景 更新购物车数量与勾选状态 业务逻辑 查询到当前用户的购物车对象Cart,更新前端传递过来的quantity与selected字段 方法一 var _ = require('underscore'); Cart.f

  • 如何go语言比较两个对象是否深度相同

    前言: func DeepEqual(x, y interface{}) bool DeepEqual 函数的参数是两个 interface,实际上也就是可以输入任意类型,输出 true 或者 flase 表示输入的两个变量是否是“深度”相等. 先明白一点,如果是不同的类型,即使是底层类型相同,相应的值也相同,那么两者也不是“深度”相等. type MyInt int type YourInt int func main() { m := MyInt(1) y := YourInt(1) fmt

  • JavaScript比较两个对象是否相等的方法

    本文实例讲述了JavaScript比较两个对象是否相等的方法.分享给大家供大家参考.具体如下: 在Python中可以通过cmp()内建函数来比较两个对象所包涵的数据是否相等(数组.序列.字典).但是在javascript语言中并没有相关的实现.本js代码通过对js对象进行各方面的比较来判断两个对象是否相等 cmp = function( x, y ) { // If both x and y are null or undefined and exactly the same if ( x ==

  • c语言实现两个值互相交换的函数

    c语言中实现两个值互换的函数. 1. #include <stdio.h> void swap(int n1, int n2) { int tmp; tmp = n1; n1 = n2; n2 = tmp; } int main(void) { int a, b; puts("please input two integers."); printf("a = "); scanf("%d", &a); printf("

  • R语言常用两种并行方法之snowfall详解

    上一篇博客(R中两种常用并行方法之parallel)中已经介绍了R中常见的一种并行包:parallel,其有着简单便捷等优势,其实缺点也是非常明显,就是很不稳定.很多时候我们将大量的计算任务挂到服务器上进行运行时,更看重的是其稳定性. 这时就要介绍R中的另一个并行利器--snowfall,这也是在平时做模拟时用的最多的一种方法. 针对上篇中的简单例子 首先是一个最简单的并行的例子,这个例子不需要载入任何依赖库.函数.对象等.相对也比较简单: library(snowfall) # 载入snowf

  • R语言常用两种并行方法之parallel详解

    目录 并行计算 在模拟时什么地方可以用到并行? 怎么在R中看我们可以使用并行? parallel(简单) 由于最近在进行一些论文的模拟,所以尝试了两种并行的方法:parallel与snowfall,这两种方法各有优缺,但还是推荐snowfall,整体较为稳定,不容易因为内存不足或者并行线程过多等原因而报错. 并行计算 并行计算: 简单来讲,就是同时使用多个计算资源来解决一个计算问题,是提高计算机系统计算速度和处理能力的一种有效手段.(参考:并行计算简介) 一个问题被分解成为一系列可以并发执行的离

  • JS判断两个对象内容是否相等的方法示例

    本文实例讲述了JS判断两个对象内容是否相等的方法.分享给大家供大家参考,具体如下: 我们知道,如果两个对象即使内容,JavaScript也会判断它们不相等.但是有时候,我们仅仅需要判断两个对象的内容是否相等.那么我们应该如何做到且考虑周全呢?比如说0和-0,null和undefined,是不相等的,NaN和NaN默认是不相等的.我写了一个isEqual方法,考虑到了诸多方面,代码如下: <!DOCTYPE html> <html> <head> <meta cha

  • go语言计算两个时间的时间差方法

    本文实例讲述了go语言计算两个时间的时间差方法.分享给大家供大家参考.具体分析如下: go语言计算两个时间的时间差,代码很简单,返回1天前.1周前还是1月前的时间 package main import ( "fmt" "time" ) func main() { //Add方法和Sub方法是相反的,获取t0和t1的时间距离d是使用Sub //将t0加d获取t1就是使用Add方法 k := time.Now() //一天之前 d, _ := time.ParseDu

  • Go语言计算两个经度和纬度之间距离的方法

    本文实例讲述了Go语言计算两个经度和纬度之间距离的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package main        import (     "fmt"     "math" )        func main() {     lat1 := 29.490295     lng1 := 106.486654            lat2 := 29.615467     lng2 := 106.581515     f

  • Python中的is和==比较两个对象的两种方法

    Python中的is和==比较两个对象的两种方法 在Python中有两种方式比较两个对象是否相等,分别是is和==,两者之间是不同的 ==比较的是值(如同java中的equals方法) is比较的是引用(可以看作比较内存地址, 类似于java中的==) 对于: >>> n = 1 >>> n is 1 True >>> b = '1' >>> b is 1 False >>> n == b False 由于1和'1'

  • java 判断两个对象是否为同一个对象实例代码

    java 判断两个对象是否为同一个对象 用"=="比较的是引用的地址,用equals比较的就是值.那我们new两个相同的对象什么属性都一样,为什么编译的时候不相同,这是因为我们调用的是父类也就是Object的equals方法,这里我们就需要重写这个equals方法. public class Test5 { public static void main(String[] args) { User mUser1 = new User("zhangsan", &quo

随机推荐