如何go语言比较两个对象是否深度相同
作者:reaperhero? 发布时间:2024-02-18 21:21:58
前言:
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
。
来源:https://studygolang.com/articles/35654?utm_source=tuicool&utm_medium=referral
猜你喜欢
- 本文实例讲述了Python实现简单的列表冒泡排序和反转列表操作。分享给大家供大家参考,具体如下:# -*- coding:utf-8 -*-
- 作者:Henny Swan / co-lead of WaSP ILG译者:JunChen / member of WaSP ILG原文:h
- 本文实例讲述了Python实现读取并保存文件的类。分享给大家供大家参考,具体如下:这个类写在一个叫class_format.py 的文件里,
- 由于最近需要做项目,需要进行分词等,查了资料之后,发现python NLTK很强大,于是就想试试看。在网上找了很多安装资料,都不太完整,下载
- 介绍海象运算符,即 := ,在 PEP 572 中被提出,并在 Python3.8 版本中发布。海象运算符的英文原名叫Assignment
- 本文实例为大家分享了JavaScript实现alert弹框的具体代码,供大家参考,具体内容如下因本人水平有限,不足之处还望大家指正。先上图:
- 1.前言选项(Options)模式是对配置(Configuration)的功能的延伸。在12章(ASP.NET Core中的配置二)Conf
- 在学习python的时候,三大“名器”对没有其他语言编程经验的人来说,应该算是一个小难点,本次博客就博主自己对装饰器、迭代器和生成器理解进行
- 本文实例为大家分享了vue-Split实现面板分割的具体代码,供大家参考,具体内容如下<template> <
- Python错误SyntaxError: unexpected EOF while parsing含义是解释器到底了都没找到它要找到的东西出
- DROP FUNCTION IF EXISTS `getPY`; DELIMITER ;; CREATE FUNCTION `getPY`(
- 用pandas计算相关系数计算相关系数用pandas,比如我想知道风速大小与风向紊乱(标准差来衡量)之间的相关系数,下面是代码:import
- 迭代器什么是迭代迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代&
- 但是怎么找到是哪个SQL语句的执行时间过长呢?可以通过MySQL Slow Log来找,详解如下。 首先找到MySQL的配置文件my.cnf
- 废话不多说了,直接给大家贴代码了。-- create functioncreate function [dbo].[fnXmlToJson]
- Python----OS 文件目录处理import osimport time# 获取当前文件的绝对路径dir_1 = os.path.ab
- 本文将介绍如何在 web 框架 Django 中使用可视化工具 Pyecharts, 看完本教程你将掌握几种动态展示可视化数据的方法!Dja
- 众所周知tensorflow造势虽大却很难用,因此推荐使用Keras,它缺省是基于tensorflow的,但通过修改keras.json也可
- 近些年随着Python语言越来越流行,越来越多的人选择Python语言作为自己的职业方向。如何在心仪公司的面试中获得好成绩,并最终成功获得o
- 最近在给某网站的后台添加一系列的统计功能,遇到很多需要按条件计数的情况。尝试了几种方法,下面简要记录,供大家参考。问题描述为使讨论简单易懂,