Go 快速开发 | 09 - Go 中的结构体的比较

500 阅读4分钟

一、结构体的比较

Java 同类型对象之间的比较

以 Java 为例,Java 中的自定义对象是通过实现 equals 方法来定义两个对象之间比较的规则(同一个的两个实例化对象)

public class TestEquals {

    public static void main(String[] args) {
        Car c1 = new Car();
        c1.carNo = "00001";
        c1.name = "Model 3";

        Car c2 = new Car();
        c2.carNo = "00001";
        c2.name = "Model 3";

        System.out.println(c1.equals(c2));
    }
}

class Car {
    public String carNo;
    public String name;

    // 定义比较规则
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        
        // 转化为同一类型
        Car car = (Car) o;

        if (!carNo.equals(car.carNo)) return false;
        return name.equals(car.name);
    }

    @Override
    public int hashCode() {
        int result = carNo.hashCode();
        result = 31 * result + name.hashCode();
        return result;
    }
}

执行 main 方法,输出结果如下:

true

如果比较使用的是 == 则输出结果为 false== 比较的是内存地址;而 equals 比较时如果重写了 equals 方法就是用 equals 方法中定义的比较规则,如果没有重写则默认使用 object 中的 equals 方法,既比较地址。

Go 同结构体对象之间的比较

Go 语言中是没有类的概念的,Go 语言中的 结构体 的作用就类似其他编译型面向对象语言中的 的作用。

结构体之间可以比较吗?

结构体之间可以比较 定义一个 Car 结构体,结构体属性中只包含基本数据类型(值类型,数组除外),并实例化 3 个对象出来

func main(){

   c1 := Car{
      CarNo: "00001",
      Name:  "TESLA",
   }

   c2 := Car{
      CarNo: "00001",
      Name:  "TESLA",
   }

   c3 := Car{
      CarNo: "00001",
      Name:  "F-Type",
   }

   fmt.Println(c1 == c2)
   fmt.Println(c1 == c3)

}

type Car struct {
   CarNo string
   Name string
}

执行 main 函数,输出结果如下:

true
false

c1 和 c2 的属性值都是相同的,直接比较输出结果为 true,c1 和 c3 的属性值不同,输出结果为 false。

在 Java 重写 equals 方法的时候会有一个对象类型转换的过程,在 Go 中如果结构体类型不同,但是属性字段和属性值相同也是可以比较的。

新增一个结构体 Truck,字段与 Car 相同,将 c2 实例化改为 Truck,再进行比较,此时编译器报错。

image.png

因此需要进行类型转换

// 其余代码不变
fmt.Println(c1 == Car(c2))

再次执行 main 函数,输出结果如下:

true
false

即使是不同的结构体,只要字段相同,且字段是基本的值类型(数组除外)都是可以进行比较的。

如果结构体字段包含了引用输入类型可以比较吗?

给 Car 结构体增加一个指针类型的字段

type Car struct {
   CarNo string
   Name string
   Battery *string
}

定义两个指针变量并实例化 c1 和 c2

b1 := "75KW"
b2 := "75KW"

c1 := Car{
   CarNo: "00001",
   Name:  "TESLA",
   Battery: &b1,
}

c2 := Car{
   CarNo: "00001",
   Name:  "TESLA",
   Battery: &b2,
}

对 c1 和 c2 进行比较,输出结果为 false, 这是因为两个结构体之间的字段的地址不同。

结构体之间不可以比较 如果结构体的字段包含了数组或者切片,即是内容相同,也是不可以比较的。

修改 Car 结构体

type Car struct {
   CarNo string
   Name string
   Features [2]string
}

修改 Car 结构体的实例化对象

c1 := Car{
   CarNo: "00001",
   Name:  "TESLA",
   Features: [2]string{"安全", "舒适"},
}

c2 := Car{
   CarNo: "00001",
   Name:  "TESLA",
   Features: [2]string{"安全", "舒适"},
}

再次对 c1 和 c2 进行比较,控制台报错如下:

# command-line-arguments
./ex12.go:14:13: cannot use []string{…} (value of type []string) as type [2]string in struct literal

总结

Go 中当结构体包含了 切片、数组、字典、函数这几种数据类型时,是不能比较的,执行比较会报错。如果需要进行比较,则不能使用 == 进行比较,需要使用 reflect.DeepEqual 方法,用于判断两个值深度是否一致:

  • 首先会判断类型,相同类型的值是深度相等的,不同类型的值永远不会深度相等。
  • 如果数组值(array)的对应元素深度相等时,数组值是深度相等的,否则不相等。
  • 如果结构体(struct)值如果其对应的字段(包括导出和未导出的字段)都是深度相等的,则深度是相等的,否则不相等
  • 如果函数(func)值如果都是零,则是深度相等;否则就不相等。
  • 如果接口(interface)值持有深度相等的具体值,则深度相等,否则相等。

本文正在参加技术专题18期-聊聊Go语言框架