结构体可以进行比较吗?
先说结论:
slicemapchannel
不同的结构体之间无法进行比较。
结论验证:
第一条很显然也很合理。那么第二条结论会发生什么事情呢?
type Value1 struct {Name string
}type Value2 struct {Name string
}func main() {v1 := Value1{Name: "张三"}v2 := Value2{Name: "张三"}if v1 == v2 {fmt.Println("张三等于张三")return}fmt.Println("张三不等于张三")
}
答案输出:
# command-line-arguments
./main.go:18:8: invalid operation: v1 == v2 (mismatched types Value1 and Value2)
引入思考:
不同结构体之间是因为类型不同导致无法比较,那么相同结构体之间是否可以进行比较呢?
type Value struct {Name stringGender string
}func main() {v1 := Value{Name: "张三", Gender: "男"}v2 := Value{Name: "张三", Gender: "男"}if v1 == v2 {fmt.Println("张三等于张三")return}fmt.Println("张三不等于张三")
}
答案输出:
张三等于张三
那么是否所有相同的结构体都可以进行比较呢?
type Value struct {Name stringGender *string
}func main() {v1 := Value{Name: "张三", Gender: new(string)}v2 := Value{Name: "张三", Gender: new(string)}if v1 == v2 {fmt.Println("张三等于张三")return}fmt.Println("张三不等于张三")
}
答案输出:
张三不等于张三
所有相同的结构体,在不包括引用类型的三个字段前提下,是可以进行比较的。但是比较结果会根据类型值的不同而不同。这里不相等的原因是因为两个字段值的地址不同。
包含引用类型的结构体如何比较?
reflectDeepEqual
func main() {v1 := Value{Name: "张三", GoodAt: []string{"唱", "跳", "rap"}}v2 := Value{Name: "张三", GoodAt: []string{"唱", "跳", "rap"}}if reflect.DeepEqual(v1, v2) {fmt.Println("张三等于张三")return}fmt.Println("张三不等于张三")
}
reflect.DeepEqual
- 相同类型的值是深度相等的,不同类型的值永远不会深度相等。
- 当数组值(array)的对应元素深度相等时,数组值是深度相等的。
- 当结构体(struct)值如果其对应的字段(包括导出和未导出的字段)都是深度相等的,则该值是深度相等的。
- 当函数(func)值如果都是零,则是深度相等;否则就不是深度相等。
- 当接口(interface)值如果持有深度相等的具体值,则深度相等。
- …
指针结构体如何比较?
当我们对结构体进行比较的时候,我们会发现字段值相同的结构体是相等的。那么指针类型的结构体是不是相等的呢?
type People struct {}func main() {a := &People{}b := &People{}fmt.Println(a == b)
}
这时候有人肯定会说,这两个结构体取地址后明显不是指向同一块内存,地址自然也不相同,怎么可能会相等呢?答案肯定是false。
答案输出:
false
这里先不给出结论,我们对这段代码进行一些改进后再输出:
type People struct {}func main() {a := &People{}b := &People{}fmt.Printf("%p\n", a)fmt.Printf("%p\n", b)fmt.Println(a == b)
}
答案输出:
0x519540
0x519540true
看到答案后是不是突然傻眼了,怎么跟我原先想的完全不一样呢?为什么把a,b两个结构体指针地址打印出来就改变了结果呢?会不会是打印之后地址改变了的原因呢?
带着这份疑惑,我们做最后一步更加细致改进。
func main() {a := new(struct{})b := new(struct{})println(a, b, a == b)c := new(struct{})d := new(struct{})fmt.Println(c, d)println(c, d, c == d)
}
答案输出:
0xc0000c7f4f 0xc0000c7f4f false
&{} &{}
0x1057540 0x1057540 true
fmt.Println
fmt.Println()reflect.TypeOf().Kind()
什么是内存逃逸呢?
简单来说就是局部变量被其他函数所捕获,那么这个局部变量就会发生内存逃逸,从栈中逃逸到堆中。
为什么逃逸后的结构体相等?
zerobase
type zerobase uintptr
因此这两个逃逸的结构体自然而然就相等了。
为什么没有逃逸的结构体不相等?
根据go语言官方写的文章看出,(这里想看文档的可以点下方的链接,看煎鱼大佬写的原版文章。)golang是有意为之,和map的无序操作一样,没有逃逸的结构体是不相等的。
==
参考文献:
煎鱼带你学golang
用 Go struct 不能犯的一个低级错误!