首页 > 其他分享 >Go 快速入门指南 - 类型比较

Go 快速入门指南 - 类型比较

时间:2022-12-22 16:58:09浏览次数:63  
标签:指南 p2 入门 person fmt go Go main 比较

概述

比较运算符 用来比较两个操作数并返回一个 bool 值,常见的比较运算符:

==    等于
!=    不等于
<     小于
<=    小于等于
>     大于
>=    大于等于

在任何比较中,第一个操作数必须可以赋值给第二个操作数的类型,反过来也一样。

不可比较类型

Go 中有 3 种数据类型不能比较,分别是 slicemapfunc,如果要比较这 3 种类型, 使用 reflect.DeepEqual 函数。

为什么 slice 不能比较

(个人猜测,待验证)

  • • 切片是引用类型,比较地址没有意义

  • • 多个切片引用同一数组时,修改时会相互影响,无法保证 key 的一致性

  • • 切片除了 len 属性外,还有 cap 属性,比较的维度没办法精确衡量

基于上述原因,官方没有支持 slice 类型的比较。

为什么 map 不能比较

(个人猜测,待验证)

  • • map 遍历是随机的,无法保证 key 的一致性

为什么 func 不能比较

(个人猜测,待验证)

  • • 函数的可变参数机制,无法保证 key 的一致性

  • • 函数参数可以为 slicemap, 这两种类型不可比较

可比较类型的具体规则

  • • 布尔值 可比较

  • • 整型 可比较

  • • 浮点型 可比较。如果两个浮点型值一样 (由 IEEE-754 标准定义),则两者相等

  • • 复数型 可比较。如果两个复数型值的 real() 方法 和 imag() 方法都相等,则两者相等

  • • 字符串 可比较

  • • 指针 可比较。如果两个指针指向相同的 地址 或者两者都为 nil,则两者相等,但是指向不同的零大小变量的指针可能不相等

  • • 通道 可比较。如果两个通道是由同一个 make 创建的 (引用的是同一个 channel 指针),或者两者都为 nil, 则两者相等

  • • 接口 可比较。interface 的内部实现包含了 2 个字段,类型 T 和 值 V。如果两个 接口 具有相同的动态类型和动态值,或者两者都为 nil, 则两者相等

  • • 结构体 可比较 (如果两个结构体的所有字段都是可比较的)。如果两个结构体对应的非空白字段相等,则两者相等

  • • 数组 可比较 (如果两个数组的所有元素都是可比较的)。如果两个数组的所有对应元素相等,则两者相等

例子

指针的比较

指向相同的地址的指针

package main

import "fmt"

func main() {
    n := 1024
    p := &n
    p2 := &n
    fmt.Printf("p == p2: %t\n", p == p2)
}

// $ go run main.go
// 输出如下
/**
  p == p2: true
*/

指向 nil 的指针

package main

import "fmt"

func main() {
    var p *string
    var p2 *string
    fmt.Printf("p == p2: %t\n", p == p2)
}

// $ go run main.go
// 输出如下
/**
  p == p2: true
*/

通道的比较

同一个 make() 创建的通道

package main

import "fmt"

func main() {
    ch := make(chan bool)
    ch2 := make(chan bool)

    p := &ch
    p2 := &ch2
    fmt.Printf("p == p2: %t\n", p == p2)

    p3 := &ch
    fmt.Printf("p == p3: %t\n", p == p3)
}

// $ go run main.go
// 输出如下
/**
  p == p2: false
  p == p3: true
*/

通道为 nil

package main

import "fmt"

func main() {
    var p *chan bool
    var p2 *chan bool

    fmt.Printf("p == p2: %t\n", p == p2)
}

// $ go run main.go
// 输出如下
/**
  p == p2: true
*/

结构体的比较

比较的前提: 两个结构体的所有字段都是可比较的,相等是指: 字段类型、字段个数、字段顺序、字段值完全一致

结构体对应的非空白字段相等

package main

import "fmt"

type person struct {
    name string
    age  int
}

func main() {
    tom := person{
        name: "Tom",
        age:  6,
    }
    jerry := person{
        name: "Jerry",
        age:  8,
    }
    fmt.Printf("tom == jerry: %t\n", tom == jerry)

    nobody := person{}
    nobody2 := person{}

    fmt.Printf("nobody == nobody2: %t\n", nobody == nobody2)
}

// $ go run main.go
// 输出如下
/**
  tom == jerry: false
  nobody == nobody2: true
*/

结构体为 nil

package main

import "fmt"

type person struct {
    name string
    age  int
}

func main() {
    var nobody person
    var nobody2 person

    fmt.Printf("nobody == nobody2: %t\n", nobody == nobody2)
}

// $ go run main.go
// 输出如下
/**
  nobody == nobody2: true
*/

接口的比较

具有相同的动态类型和动态值

package main

import "fmt"

type person struct {
    name string
}

func main() {
    var tom1, tom2 interface{}

    tom1 = &person{"Tom"}
    tom2 = &person{"Tom"}

    var tom3, tom4 interface{}
    tom3 = person{"Tom"}
    tom4 = person{"Tom"}

    fmt.Printf("tom1 == tom2: %t\n", tom1 == tom2) // false
    fmt.Printf("tom3 == tom4: %t\n", tom3 == tom4) // true
}

// $ go run main.go
// 输出如下
/**
  tom1 == tom2: false
  tom3 == tom4: true
*/

上面的示例代码中,tom1 和 tom2 对应的类型是 *person,值是 person 结构体的地址,但是两个地址不同,因此两者不相等, tom3 和 tom4 对应的类型是 person,值是 person 结构体且各字段相等,因此两者相等。

接口为 nil

package main

import "fmt"

func main() {
    var tom1, tom2 interface{}
    fmt.Printf("tom1 == tom2: %t\n", tom1 == tom2) // true
}

// $ go run main.go
// 输出如下
/**
  tom1 == tom2: true
*/

小结

本小节介绍了 Go 的比较运算符以及各种数据类型的比较规则。Go 中大多数数据类型都是可以比较的, 除了 slicemapfunc 这 3 种,对于不能比较的原因,笔者给出了一些猜测,感兴趣的读者可以自行验证。限于时间和篇幅,没有给出所有数据类型的代码示例,读者可以编写代码验证具体的类型比较规则。

reference

1. https://go.dev/ref/spec#Comparison_operators

2. https://www.ruangdeveloper.com/blog/tags/golang

联系我

公众号

标签:指南,p2,入门,person,fmt,go,Go,main,比较
From: https://www.cnblogs.com/duanbiaowu/p/16998865.html

相关文章

  • Go 快速入门指南 - 通道方向和关闭通道
    概述建议先阅读 阻塞通道 和 非阻塞通道 小节。在前面的两个小节中,为了最小化代码达到演示效果,省略了 关闭通道 的步骤,正确的做法应该是在通道使用完成后关闭。......
  • Go 快速入门指南 - 互斥锁和定时器
    互斥锁对于任一共享资源,同一时间保证只有一个操作者,这种方法称为 互斥机制。关键字 Mutex 表示互斥锁类型,它的 Lock 方法用于获取锁,Unlock 方法用于释放锁。在 Lo......
  • Go 快速入门指南 - 遍历通道
    概述建议先阅读 range, 阻塞通道, 非阻塞通道 等小节。range 除了可以遍历字符串、切片、数组等数据结构外,还可以遍历通道。语法规则和遍历其他数据结构不同,遍历......
  • Go 快速入门指南 - 结构体
    概述结构体 是将零个或多个字段(变量)组合在一起的复合数据类型,类似于面向对象语言中的 对象。结构体以及其字段都使用 可见性 规则。语法规则type 结构体名称......
  • Go 快速入门指南 - 嵌套结构体
    概述Go支持将多个结构体通过嵌套的方式,组成一个大的结构体,降低了单个结构体复杂度,同时提高了结构体之间组合的灵活性。例子为了省略篇幅,本小节只使用 字面量 方式初......
  • Go 快速入门指南 - 函数
    概述函数 是将一个或者一类问题包装为一个代码块,可以被多次调用,提高代码重用性。Go函数中声明、定义、参数、返回值这些基础概念,和其他编程语言中的一致,这里不再赘述。......
  • Go 快速入门指南 - 接口和方法
    接口Go接口是隐式实现。 对于一个数据类型,无需声明它实现了哪些接口,只需要实现接口必需的方法即可。当然了,存在一个小问题就是:我们可能无意间实现了某个接口:),所以 ......
  • Go 快速入门指南 - init 函数
    概述init()函数 是一个特殊的函数,一般称为初始化函数,不能被调用。 在每个文件里面,当程序启动或者文件被作为包引用的时候,init()函数就会自动执行,一般用来做一些包的......
  • Go 快速入门指南 - 零值和错误
    零值当一个变量使用 var 进行声明后并未进行初始化(变量后面没有赋值符 =)操作,会默认分配一个零值(zerovalue)。不同类型对应的零值类型零值boolfalse......
  • Go 快速入门指南 - 判断是否实现接口
    判断是否实现接口Go里面没有 implements 关键字来判断一个结构体(对象)是否实现了某个接口, 要实现类似的功能,需要用到 类型断言。类型断言// 判断 v 是否实......