序言
众所周知,在 go 语言里面,有一个叫做等于号 ==
的东西,平时微不足道,如果不是原生 go 语言程序员,甚至都不一定能注意得到它,不就是一个等号么? 有什么不一样的? 但是你知道么,go的等号,可不仅仅只能在基础类型中进行比较
基础类型
这个简单,也是 go 语言的等号最常常用到的地方
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 14:18:22
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
func main() {
var a, b, c int = 12, 12, 34
fmt.Println(a == b)
fmt.Println(b == c)
}
但是你也应当注意,在go语言或者其他的语言中,我们都不应该将两个 float 类型进行等于运算,这是因为计算机中的数据存储是以二进制的形式存储的,在进行加减运算时难免会遇到精度丢失的问题,所以如果要将两个小数进行比较或者其他运算,建议将这个小数转换成string
类型后处理(参考大整数相加的算法),这个问题以后有空了我再来细说。
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 14:20:18
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
func main() {
var a, b, c = 0.1, 0.2, 0.3
fmt.Println(a+b == c) // false
fmt.Println(a + b) // 0.30000000000000004
fmt.Println(c) // 0.3
}
然后还有一点值得注意的问题,这个虽然不常用,但是数组之间也是可以使用等于符号的哦
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 14:47:27
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
type INT1 int
type INT2 int
func main() {
var a, b = [10]int{1, 2, 3, 4, 5, 6}, [10]int{1, 2, 3, 4, 5, 6}
fmt.Println(a == b) // true
}
自定义类型
在 go 语言中,我们可以通过
type typeA typeB
type typeC typeB
来自定义一个类型 typeA 和一个 typeC,那你有没有想过,因为 typeA 和 typeC 实质都是 typeB,那么此时的 typeA 和 typeC,可以进行比较么?答案当然是否定的,我们看下面这个代码片段
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 13:57:20
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
type INT1 int
type INT2 int
func main() {
var a INT1 = 12
var b INT2 = 12
fmt.Println(a == b) // invalid operation: a == b (mismatched types INT1 and INT2)
}
go 语言在进行比较之前,会先检查等于号两边的变量是否是相同类型的,如果类型不相同,则不能进行比较,在上述这个例子中,尽管a和b的底层都是 int 类型,但是在进行等于运算的时候,go 依然会认为a和b的类型是 INT1 和 INT2
当然,作为一个 go 程序员,你也应该要想到这些:
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 14:35:54
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
type INT1 int
type INT2 int
func main() {
var a INT1 = 12
var b INT2 = a // cannot use a (variable of type INT1) as type INT2 in variable declaration
fmt.Println(a == b)
}
聪明的你可能会发现另外一个问题,既然我们将 a 赋值给 b 失败了,那为什么我们将 12 赋值给 a 会成功呢? 其实,12 这个值是一个常量,go 编译器在编译的时候做了一些额外的操作以允许您将基础类型的常量直接赋值给其自定义类型的变量。 但是如果反过来,将 Int1 类型赋值给 int 类型,则是不被允许的。
引用类型
说到引用类型,我们再熟悉不过了,我们先回忆下有哪些:指针
、切片
、字典
、函数
、通道
我们前文说了,数组也能比较,那我们看看与数组比较类似的切片能比较么:
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 14:51:24
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
func main() {
var a, b = []int{1, 2, 3, 4, 5, 6}, []int{2, 2, 3, 4, 5, 6}
fmt.Println(a == b) // invalid operation: a == b (slice can only be compared to nil)
}
切片不行,那我map总行了吧:
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 14:53:10
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
func main() {
var a = map[string]string{"hello": "world"}
var b = map[string]string{"hello": "world"}
fmt.Println(a == b) // invalid operation: a == b (map can only be compared to nil)
}
出现这个问题的原因很简单,因为哈希表本身是一个不保证有序的结构,即使哈希表的键值对都相同,它们在内存中的实际布局上也存在差异。 以此 Go 不支持直接判断两个 map 是否相等。
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 15:00:54
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
func main() {
var ch1, ch2 = make(chan int, 10), make(chan int, 10)
var ch3 chan int = ch1
ch1 <- 1
ch2 <- 2
fmt.Println(ch1 == ch2) // false
fmt.Println(ch1 == ch3) // true
}
结局很意外,channel竟然比较上了,而且从这段代码里面,我们可以知道,channel的比较,是基于比较引用类型底层的指针来实现的,如果两个channel的底层的指针是一样的,那么channel的比较结果及时true,反之则为false
那现在就有一个问题,为什么map和slice不可进行比较呢?我们先说说slice
前文我们也提过,slice的底层结构如下:
type slice struct {
array unsafe.Pointer // 指向底层数组
len int // slice 长度
cap int // slice 底层数组容量|预分配的大小
}
如果我们要进行比较,那么是不是应该比较指针、len、以及cap?那判断两个slice是否相等,是不是应该让三个值都相等?那这样判断是否还存在意义?因此设计go语言的时候,设计者就没有考虑切片的比较问题
我们再说说map,map的value因为可以是切片等等类型,所以在比较的时候会出现一些问题,因此go的设计也也没有考虑map的比较问题
函数类型也是,这个函数类型也是不可比较的
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 14:56:50
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
func main() {
var fun1 = func() int {
return 1
}
var fun2 = func() int {
return 2
}
fmt.Println(fun1 == fun2) // invalid operation: fun1 == fun2 (func can only be compared to nil)
}
接口类型
go 语言中的接口类型也是非常有特点的,我们来看看下面的一段代码
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 15:53:03
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
func compare(a, b interface{}) bool {
return a == b
}
func main() {
var a, b = 12, 12
var c string = "hello"
fmt.Println(compare(a, b)) // true
fmt.Println(compare(a, c)) // false
}
go中的interface都有默认实现一个T(类型)和一个V(值),比较的时候会先比较interface的T,如果相同继续比较V,否则就返回false
这里说一下,一下这种情况也会出错:
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 17:44:20
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
func compare(a, b interface{}) bool {
return a == b
}
func main() {
var a = []int{1, 2, 4}
var b = []int{1, 2, 4}
fmt.Println(compare(a, b)) // panic: runtime error: comparing uncomparable type []int
}
结构体
go语言的结构体也是可以比较的,还是和基础类型一样,会优先比较数据类型,如果数据类型不同就会报错,否则会一个一个去比较内部的值是否相等,参考下面代码
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 17:47:03
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
type person struct {
Age int
Name string
}
type Vampare struct {
Age int
Name string
}
func main() {
var p1 = person{Age: 12, Name: "dio"}
var p2 = Vampare{Age: 12, Name: "dio"}
fmt.Println(p1 == p2) // invalid operation: p1 == p2 (mismatched types person and Vampare)
}
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 13:58:54
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
type person struct {
Age int
Name string
}
func main() {
var p1 = person{Age: 12, Name: "dio"}
var p2 = person{Age: 12, Name: "dio"}
fmt.Println(p1 == p2) // true
}
值得一提的是,如果结构体中出现了无法比较的字段,比如出现了slice或者map之类的东西,还是会报错
/*
* @Author: NorthCity1984
* @LastEditTime: 2022-04-25 17:50:09
* @Description:
* @Website: https://grimoire.cn
* Copyright (c) NorthCity1984 All rights reserved.
*/
package main
import "fmt"
type person struct {
Age int
Name string
Box []int
}
func main() {
var p1 = person{Age: 12, Name: "dio", Box: []int{1, 2, 3, 4}}
var p2 = person{Age: 12, Name: "dio", Box: []int{1, 2, 3, 4}}
fmt.Println(p1 == p2) // invalid operation: p1 == p2 (struct containing []int cannot be compared)
}