Go语言 如何比较两个结构体、切片或Map是否相等?

tcomlyy6  于 2023-09-28  发布在  Go
关注(0)|答案(7)|浏览(114)

我想检查两个结构体、切片和Map是否相等。
但是我在下面的代码中遇到了问题。请参阅我在相关行的评论。

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

q5lcpyga

q5lcpyga1#

你可以使用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))
z8dt9xmd

z8dt9xmd2#

reflect.DeepEqual经常被错误地用来比较两个类似的结构体,就像你的问题中那样。
cmp.Equal是比较结构体的更好工具。
要了解为什么反射是不明智的,让我们看看文档:
如果结构值的相应字段(包括导出的和未导出的字段)完全相等,则结构值完全相等。
....
numbers、bool、string和channels -如果使用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时要小心,因为它是may lead to a panic condition
它只用于测试,因为性能不是目标,如果不能比较值,它可能会死机。它的恐慌倾向意味着它不适合生产环境,在那里虚假的恐慌可能是致命的。

mjqavswn

mjqavswn3#

下面是如何滚动自己的函数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
}

更新:Go 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)
}
jogvjijk

jogvjijk4#

如果你打算在测试中使用它,由于July 2017,你可以使用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
}
fcg9iug3

fcg9iug35#

如果您在单元测试**中比较它们,一个方便的替代方法是testify中的EqualValues函数。

y1aodyip

y1aodyip6#

如果你想比较简单的一级结构,最好和最简单的方法是if语句。
像这样if s1 == s2

举个简单的例子:

type User struct { 
    name      string 
    email           string
} 

func main() {
    u1 := User{
            name: "Iron Man", 
            email: "[email protected]",
    }
    u2 := User{
            name: "Iron Man", 
            email: "[email protected]",
    }
        // 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
你可以玩这个here

mi7gmzs6

mi7gmzs67#

Go 1.21

和下面一样,但是maps.Equal现在在标准库中,你可以直接使用它,而不是导入实验性的软件包或自己开发。

转到1.18 ~ 1.20

这个提议(https://github.com/golang/go/issues/47649)是Go泛型未来实现的一部分,它引入了一个新函数来比较两个map,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

maps软件包位于golang.org/x/exp/maps中。这是实验性的,超出了Go兼容性保证。他们的目标是将其移动到Go 1.19的标准库中
你可以在gotip playground https://gotipplay.golang.org/p/M0T6bCm1_3m中看到它的工作

相关问题