如何比较两个结构、切片或映射是否相等?

我想检查两个结构,切片和映射是否相等。

但是下面的代码遇到了问题。请在相关行中查看我的评论。

package main


import (
"fmt"
"reflect"
)


type T struct {
X int
Y string
Z []int
M map[string]int
}


func main() {
t1 := T{
X: 1,
Y: "lei",
Z: []int{1, 2, 3},
M: map[string]int{
"a": 1,
"b": 2,
},
}


t2 := T{
X: 1,
Y: "lei",
Z: []int{1, 2, 3},
M: map[string]int{
"a": 1,
"b": 2,
},
}


fmt.Println(t2 == t1)
//error - invalid operation: t2 == t1 (struct containing []int cannot be compared)


fmt.Println(reflect.ValueOf(t2) == reflect.ValueOf(t1))
//false
fmt.Println(reflect.TypeOf(t2) == reflect.TypeOf(t1))
//true


//Update: slice or map
a1 := []int{1, 2, 3, 4}
a2 := []int{1, 2, 3, 4}


fmt.Println(a1 == a2)
//invalid operation: a1 == a2 (slice can only be compared to nil)


m1 := map[string]int{
"a": 1,
"b": 2,
}
m2 := map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(m1 == m2)
// m1 == m2 (map can only be compared to nil)
}

http://play.golang.org/p/azizw2wuni.

205307 次浏览

您可以使用Reflect.DeepEqual,也可以实现自己的函数(在性能方面比使用反射更好):

http://play.golang.org/p/cpdfsygny_.

m1 := map[string]int{
"a":1,
"b":2,
}
m2 := map[string]int{
"a":1,
"b":2,
}
fmt.Println(reflect.DeepEqual(m1, m2))

下面是您如何http://play.golang.org/p/Qgw7XuLNhb运行自己的函数

func compare(a, b *T) bool {
if a == b {
return true
}
if a.X != b.X || a.Y != b.Y {
return false
}
if len(a.Z) != len(b.Z) || len(a.M) != len(b.M) {
return false
}
for i, v := range a.Z {
if b.Z[i] != v {
return false
}
}
for k, v := range a.M {
if b.M[k] != v {
return false
}
}
return true
}

更新:去1.18

import (
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)


func compare(a, b *T) bool {
if a == b {
return true
}
if a.X != b.X {
return false
}
if a.Y != b.Y {
return false
}
if !slices.Equal(a.Z, b.Z) {
return false
}
return maps.Equal(a.M, b.M)
}

reflect.DeepEqual经常被错误地用来比较两个相似的结构,就像你的问题一样。

cmp.Equal是比较结构的更好工具。

要了解为什么反射是不明智的,让我们看看文档

如果结构值对应的字段(导出的和未导出的)深度相等,则结构值深度相等。

....

数字、布尔值、字符串和通道-如果使用go的==运算符相等,则深度相等。

如果我们比较相同UTC时间的两个time.Time值,则如果它们的元数据时区不同,t1 == t2将为false.

go-cmp查找Equal()方法,并使用该方法正确比较时间。

示例:

m1 := map[string]int{
"a": 1,
"b": 2,
}
m2 := map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(cmp.Equal(m1, m2)) // will result in true

重要注意事项:

使用cmp.Equal时要小心,因为它可能导致恐慌状况

它仅用于测试,因为性能不是目标 如果不能比较这些值,它可能会出现混乱。它的倾向 走向恐慌意味着它不适合生产。 虚假的恐慌可能是致命的环境。

如果您单元测试中来比较它们,一个方便的替代方法是在作证中使用等值函数。

如果您打算在测试中使用它,由于2017年7月,您可以将cmp.Equalcmpopts.IgnoreFields选项一起使用。

func TestPerson(t *testing.T) {
type person struct {
ID   int
Name string
}


p1 := person{ID: 1, Name: "john doe"}
p2 := person{ID: 2, Name: "john doe"}
println(cmp.Equal(p1, p2))
println(cmp.Equal(p1, p2, cmpopts.IgnoreFields(person{}, "ID")))


// Prints:
// false
// true
}

如果要比较简单的一级结构,最好且最简单的方法是if语句。

像这样的if s1 == s2

这里有一个简单的例子:

type User struct {
name      string
email           string
}


func main() {
u1 := User{
name: "Iron Man",
email: "ironman@avengers.com",
}
u2 := User{
name: "Iron Man",
email: "ironman@avengers.com",
}
// Comparing 2 structs
if u1 == u2 {
fmt.Println("u1 is equal to u2")
} else {
fmt.Println("u1 is not equal to u2")
}
}

结果:u1 is equal to u2

您可以使用此在这里进行游戏。

比较地图的新方法

该提案(https://github.com/golang/go/issues/47649)是GO泛型的未来实现的一部分,它引入了一个新功能来比较两个映射,maps.Equal

// Equal reports whether two maps contain the same key/value pairs.
// Values are compared using ==.
func Equal[M1, M2 constraints.Map[K, V], K, V comparable](m1 M1, m2 M2) bool

使用示例

strMapX := map[string]int{
"one": 1,
"two": 2,
}
strMapY := map[string]int{
"one": 1,
"two": 2,
}


equal := maps.Equal(strMapX, strMapY)
// equal is true

golang.org/x/exp/maps中找到maps程序包。这是实验性的,超出了GO兼容性保证。他们的目标是将其移到GO 1.19中的STD库中

你可以看到它在GOTIP游乐场工作。 https://gotipplay.golang.org/p/M0T6bCm1_3m