结构体可以进行比较吗?

先说结论:

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
0x519540

true

看到答案后是不是突然傻眼了,怎么跟我原先想的完全不一样呢?为什么把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 不能犯的一个低级错误!