平时开发中对比两个struct
或者map
、slice
是否相等是经常遇到的,有很多对比的方式,比如==
,reflect.DeepEqual()
,cmp.Equal()
等也是经常容易混淆的,这么多种对比方式,适用场景和优缺点都有哪些呢?为什么可以用==
,有的却不可以呢?问题多多,今天我们来具体总结一下,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
相信==
判等操作,大家每天都在用。golang
中对==
的处理有一些细节的地方需要特别注意,==
操作最重要的一个前提是:两个操作数类型必须相同!如果类型不同,那么编译时就会报错。
示例代码:
package main import "fmt" func main() { var a int32 var b int64 // 编译错误:invalid operation a == b (mismatched types int32 and int64) fmt.Println(a == b) }
经常见到使用==
的类型一般是:string
,int
等基本类型。struct
有时候可以用有时候不可以。slice
和map
使用 ==
会报错。
因为slice和map不止是需要比较值,还需要比较len和cap,层级比较深的话还需要递归比较,不是简单的==就可以比较的,所以他们各自之间是不可以直接用==比较的,slice和map只能和nil使用==。
s1 := []int64{1, 3} s2 := []int64{1, 2} if s1 == nil {} //编辑器不会提示报错 if s1 == s2 {} //编辑器会提示报错
channel是引用类型,对比的是存储数据的地址。channel是可以使用==的,只要类型一样就可以。
ch1 := make(chan int, 1) ch2 := ch1 if cha2 == cha1{fmt.Println("true")} // true
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存。
实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的
内存是完全独立的。对结构体进行&取地址操作时,视为对该类型进行一次 new 的实例化操作
因此:go中的结构体: v = Struct {}, v = &Struct{} 这个两种写法是等价的。
示例代码:
package main import ( "fmt" ) type User struct { Name string Age int64 } type People struct { Name string Hobby []string } func main() { p1 := People{Name: "test", Hobby: []string{"唱", "跳"}} p2 := People{Name: "test", Hobby: []string{"唱", "跳"}} u1 := User{Name: "test", Age:18} u2 := User{Name: "test", Age:18} if p1 == p2 { fmt.Println("p1 == p2") //报错 } if u1 == u2 { fmt.Println("u1 == u2") } }
reflect包提供的深度对比(递归)的方法,适用于go中的slice,map,struct,function的对比。
对比规则
array
的对应元素深度相等时,数组值是深度相等的。struct
值如果其对应的字段(包括导出和未导出的字段)都是深度相等的,则该值是深度相等的。func
值如果都是零,则是深度相等;否则就不是深度相等。interface
值如果持有深度相等的具体值,则深度相等。slice
序号相同,如果值,指针都相等,那么就是深度相等的map
相同的key,如果值,指针都相等,那么就是深度相等的。通过以上规则可以看到,reflect.DeepEqual
是可以比较struct
的,同时也可以用来比较slice
和map
。
示例代码:
package main import ( "fmt" "reflect" ) type People struct { Name string Hobby []string } func main() { p1 := People{Name: "test", Hobby: []string{"唱", "跳"}} p2 := People{Name: "test", Hobby: []string{"唱", "跳"}} if reflect.DeepEqual(p1, p2) { fmt.Println("struct true") } mp1 := map[int]int{1: 1, 2: 2} mp2 := map[int]int{1: 1, 2: 2} if ok := reflect.DeepEqual(mp1, mp2);ok { fmt.Println("mp1 == mp2!") } else { fmt.Println("mp1 != mp2!") } }
go-cmp是 Google 开源的比较库,它提供了丰富的选项。
对比规则
示例代码:
package main import ( "fmt" "github.com/google/go-cmp/cmp" ) type Contact struct { Phone string Email string } type User struct { Name string Age int Contact *Contact } func main() { u1 := User{Name: "test", Age: 18} u2 := User{Name: "test", Age: 18} fmt.Println("u1 == u2?", u1 == u2) //true fmt.Println("u1 equals u2?", cmp.Equal(u1, u2)) //true c1 := &Contact{Phone: "123456789", Email: "dj@example.com"} c2 := &Contact{Phone: "123456789", Email: "dj@example.com"} u1.Contact = c1 u2.Contact = c1 fmt.Println("u1 == u2 with same pointer?", u1 == u2) //true fmt.Println("u1 equals u2 with same pointer?", cmp.Equal(u1, u2)) //true u2.Contact = c2 fmt.Println("u1 == u2 with different pointer?", u1 == u2) //false fmt.Println("u1 equals u2 with different pointer?", cmp.Equal(u1, u2)) //true }
简单类型的==
对比速度最快
复杂类型,自己知道结构之后写的自定义对比速度次之
复杂结构且不确定结构的,使用cmp.Equal()或者reflect.DeepEqual()都可以,就是效率低点
assert.Equal()底层使用的就是reflect.DeepEqual()
对于比较两个struct或者map,slice是否相等,方式很多,效率也有差异。选择合适自己需求的最重要。相对来说,cmp包是要更安全且可操作性更强一点。