Golang中的struct比较

1. 可排序、可比较和不可比较

  • 可排序的数据类型有三种
    • 整型Integer
    • 浮点型Floating-point
    • 字符串String
  • 可比较的数据类型除了上述三种外,还有Boolean,Complex,Pointer,Channel,Interface和Array
  • 不可比较的数据类型包括,Slice, Map, 和Function

2. 同一个struct的2个实例比较

  • 包含不可比较的成员变量(map,slice,func等)
    • ==操作符可以比较指针,不能直接比较实例;
  • 不包含不可比较的成员变量
    • ==操作符可以比较指针和实例;
  • reflect.DeepEqual函数,指针和实例均可以比较(无论有没有包含不可比较的成员变量)
package main

import (
	"fmt"
	"reflect"
)

type T1 struct {
	Name  string
	Age   int
	Arr   [2]bool
	ptr   *int
	slice []int
	map1  map[string]string
}

func main() {
	t1 := T1{
		Name:  "yxc",
		Age:   1,
		Arr:   [2]bool{true, false},
		ptr:   new(int),
		slice: []int{1, 2, 3},
		map1:  make(map[string]string, 0),
	}

	t2 := T1{
		Name:  "yxc",
		Age:   1,
		Arr:   [2]bool{true, false},
		ptr:   new(int),
		slice: []int{1, 2, 3},
		map1:  make(map[string]string, 0),
	}

	// 报错 实例不能比较 Invalid operation: t1 == t2 (operator == not defined on T1)
	// fmt.Println(t1 == t2)
	// 指针可以比较
	fmt.Println(&t1 == &t2) // false
	fmt.Println(reflect.DeepEqual(t1, t2)) // true

	t3 := &T1{
		Name:  "yxc",
		Age:   1,
		Arr:   [2]bool{true, false},
		ptr:   new(int),
		slice: []int{1, 2, 3},
		map1:  make(map[string]string, 0),
	}

	t4 := &T1{
		Name:  "yxc",
		Age:   1,
		Arr:   [2]bool{true, false},
		ptr:   new(int),
		slice: []int{1, 2, 3},
		map1:  make(map[string]string, 0),
	}

	fmt.Println(t3 == t4)                  // false
	fmt.Println(reflect.DeepEqual(t3, t4)) // true
	fmt.Printf("%p, %p \n", t3, t4)        // 0xc000046050, 0xc0000460a0
	fmt.Printf("%p, %p \n", &t3, &t4)      // 0xc000006030, 0xc000006038

	// 前面加*,表示指针指向的值,即结构体实例,不能用==
	// Invalid operation: *t3 == *t4 (operator == not defined on T1)
	// fmt.Println(*t3 == *t4)

	t5 := t3
	fmt.Println(t3 == t5)                  // true
	fmt.Println(reflect.DeepEqual(t3, t5)) // true
	fmt.Printf("%p, %p \n", t3, t5)        // 0xc000046050, 0xc000046050
	fmt.Printf("%p, %p \n", &t3, &t5)      // 0xc000006030, 0xc000006040
}

3. 两个不同的struct的实例能不能比较

3.1 两个struct可以相互转换,且不包含不可比较的成员变量,转换成相同类型后可以使用==操作符

package main

import (
	"fmt"
)

type T2 struct {
	Name string
	Age  int
	Arr  [2]bool
	ptr  *int
}

type T3 struct {
	Name string
	Age  int
	Arr  [2]bool
	ptr  *int
}

func main() {
	var ss1 T2
	var ss2 T3

	// Cannot use 'ss2' (type T3) as type T2 in assignment
	//ss1 = ss2

	ss3 := T2(ss2)
	fmt.Println(ss3 == ss1) // true
}

3.2 两个struct可以相互转换,但包含不可比较的成员变量

  • 转化成相同类型后,不可以比较实例,可以比较指针;
  • 一定要转化后再使用reflect.DeepEqual函数进行比较,否则总是返回false,失去了比较的意义;
package main

import (
	"fmt"
	"reflect"
)

type T2 struct {
	Name string
	Age  int
	Arr  [2]bool
	ptr  *int
	map1 map[string]string
}

type T3 struct {
	Name string
	Age  int
	Arr  [2]bool
	ptr  *int
	map1 map[string]string
}

func main() {
	var ss2 T2
	var ss3 T3

	// 不同类型的struct, 不可以直接用=赋值
	// Cannot use 'ss2' (type T2) as type T3 in assignment
	// ss2 = ss3

	// invalid operation: &ss2 == &ss3 (mismatched types *T2 and *T3)
	//fmt.Println(&ss2==&ss3)

	// invalid operation: ss2 == ss3 (mismatched types T2 and T3)
	//fmt.Println(ss2==ss3)

	fmt.Println(reflect.DeepEqual(ss3, ss2)) // false
	fmt.Println(reflect.DeepEqual(&ss3, &ss2)) // false

	ss4 := T2(ss3)
	// Invalid operation: ss4==ss2 (operator == not defined on T2)
	//fmt.Println(ss4==ss2)   //含有不可比较成员变量

	// 转化成相同类型后, 可以比较指针
	fmt.Println(&ss4==&ss2) // false
	fmt.Println(reflect.DeepEqual(ss4, ss2)) // true
}

4. struct可以作为map的key么?

  • struct必须是可比较的,才能作为key,否则编译时报错
package main

import (
	"fmt"
)

type T1 struct {
	Name  string
	Age   int
	Arr   [2]bool
	ptr   *int
	//test  func(i int) (err error)
	//slice []int
	//map1  map[string]string
}

type T2 struct {
	Name string
	Age  int
	Arr  [2]bool
	ptr  *int
}

func main() {
	n := make(map[T2]string, 0) // 无报错
	fmt.Print(n)                // map[]
	// lnvalid map key type: the comparison operators == and != must be fully defined for key type
	m := make(map[T1]string, 0)
	fmt.Println(m)
}

5. 参考资料