相信==
判等操做,你們天天都在用。以前在論壇上看到很多人在問 golang ==
比較的結果。看到不少人對 golang 中==
的結果不太瞭解。確實,golang 中對==
的處理有一些細節的地方須要特別注意。雖然平時可能不太會遇到,可是碰到了就是大坑。本文將對 golang 中==
操做作一個系統的介紹。但願能對你們有所幫助。git
golang 中的數據類型能夠分爲如下 4 大類:github
int/uint/int8/uint8/int16/uint16/int32/uint32/int64/uint64/byte/rune
等)、浮點數(float32/float64
)、複數類型(complex64/complex128
)、字符串(string
)。error
。==
操做最重要的一個前提是:兩個操做數類型必須相同!類型必須相同!類型必須相同!golang
若是類型不一樣,那麼編譯時就會報錯。編程
注意:數組
C/C++
中的隱式類型轉換。雖然寫起來稍微有些麻煩,可是能避免從此很是多的麻煩!!!type
定義新類型。新定義的類型與底層類型不一樣,不能直接比較。爲了更容易看出類型,示例代碼中的變量定義都顯式指定了類型。bash
看下面的代碼:編程語言
package main
import "fmt"
func main() {
var a int8
var b int16
// 編譯錯誤:invalid operation a == b (mismatched types int8 and int16)
fmt.Println(a == b)
}
複製代碼
沒有隱式類型轉換。ui
package main
import "fmt"
func main() {
type int8 myint8
var a int8
var b myint8
// 編譯錯誤:invalid operation a == b (mismatched types int8 and myint8)
fmt.Println(a == b)
}
複製代碼
雖然myint8的底層類型是int8,可是他們是不一樣的類型。spa
下面依次經過這 4 種類型來講明==
是如何作比較的。設計
這是最簡單的一種類型。比較操做也很簡單,直接比較值是否相等。沒啥好說的,直接看例子。
var a uint32 = 10
var b uint32 = 20
var c uint32 = 10
fmt.Println(a == b) // false
fmt.Println(a == c) // true
複製代碼
有一點須要注意,浮點數的比較問題:
var a float64 = 0.1
var b float64 = 0.2
var c float64 = 0.3
fmt.Println(a + b == c) // false
複製代碼
由於計算機中,有些浮點數不能精確表示,浮點運算結果會有偏差。若是咱們分別輸出a+b
和c
的值,會發現它們確實是不一樣的:
fmt.Println(a + b)
fmt.Println(c)
// 0.30000000000000004
// 0.3
複製代碼
這個問題不是 golang 獨有的,只要浮點數遵循 IEEE 754 標準的編程語言都有這個問題。須要特別注意,儘可能不要作浮點數比較,確實須要比較時,計算兩個浮點數的差的絕對值,若是小於必定的值就認爲它們相等,好比1e-9
。
複合類型也叫作聚合類型。golang 中的複合類型只有兩種:數組和結構體。它們是逐元素/字段比較的。
注意:數組的長度視爲類型的一部分,長度不一樣的兩個數組是不一樣的類型,不能直接比較。
例如:
a := [4]int{1, 2, 3, 4}
b := [4]int{1, 2, 3, 4}
c := [4]int{1, 3, 4, 5}
fmt.Println(a == b) // true
fmt.Println(a == c) // false
type A struct {
a int
b string
}
aa := A { a : 1, b : "test1" }
bb := A { a : 1, b : "test1" }
cc := A { a : 1, b : "test2" }
fmt.Println(aa == bb)
fmt.Println(aa == cc)
複製代碼
引用類型是間接指向它所引用的數據的,保存的是數據的地址。引用類型的比較實際判斷的是兩個變量是否是指向同一份數據,它不會去比較實際指向的數據。
例如:
type A struct {
a int
b string
}
aa := &A { a : 1, b : "test1" }
bb := &A { a : 1, b : "test1" }
cc := aa
fmt.Println(aa == bb)
fmt.Println(aa == cc)
複製代碼
由於aa
和bb
指向的兩個不一樣的結構體,雖然它們指向的值是相等的(見上面複合類型的比較),可是它們不等。 aa
和cc
指向相同的結構體,因此它們相等。
再看看channel
的比較:
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
ch3 := ch1
fmt.Println(ch1 == ch2)
fmt.Println(ch1 == ch3)
複製代碼
ch1
和ch2
雖然類型相同,可是指向不一樣的channel
,因此它們不等。 ch1
和ch3
指向相同的channel
,因此它們相等。
關於引用類型,有兩個比較特殊的規定:
nil
值比較。map
之間不容許比較。map
只能與nil
值比較。爲何要作這樣的規定?咱們先來講切片。由於切片是引用類型,它能夠間接的指向本身。例如:
a := []interface{}{ 1, 2.0 }
a[1] = a
fmt.Println(a)
// !!!
// runtime: goroutine stack exceeds 1000000000-byte limit
// fatal error: stack overflow
複製代碼
上面代碼將a
賦值給a[1]
致使遞歸引用,fmt.Println(a)
語句直接爆棧。
基於上面兩點緣由,golang 直接規定切片類型不可比較。使用==
比較切片直接編譯報錯。
例如:
var a []int
var b []int
// invalid operation: a == b (slice can only be compared to nil)
fmt.Println(a == b)
複製代碼
錯誤信息很明確。
由於map
的值類型可能爲不可比較類型(見下面,切片是不可比較類型),因此map
類型也不可比較🤣。
接口類型是 golang 中比較重要的一種類型。接口類型的值,咱們稱爲接口值。一個接口值是由兩個部分組成的,具體類型(即該接口存儲的值的類型)和該類型的一個值。引用《go 程序設計語言》的名稱,分別稱爲動態類型和動態值。接口值的比較涉及這兩部分的比較,只有當動態類型徹底相同且動態值相等(動態值使用==
比較),兩個接口值纔是相等的。
例如:
var a interface{} = 1
var b interface{} = 2
var c interface{} = 1
var d interface{} = 1.0
fmt.Println(a == b) // false
fmt.Println(a == c) // true
fmt.Println(a == d) // false
複製代碼
a
和b
動態類型相同(都是int
),動態值也相同(都是1
,基本類型比較),故二者相等。 a
和c
動態類型相同,動態值不等(分別爲1
和2
,基本類型比較),故二者不等。 a
和d
動態類型不一樣,a
爲int
,d
爲float64
,故二者不等。
type A struct {
a int
b string
}
var aa interface{} = A { a: 1, b: "test" }
var bb interface{} = A { a: 1, b: "test" }
var cc interface{} = A { a: 2, b: "test" }
fmt.Println(aa == bb) // true
fmt.Println(aa == cc) // false
var dd interface{} = &A { a: 1, b: "test" }
var ee interface{} = &A { a: 1, b: "test" }
fmt.Println(dd == ee) // false
複製代碼
aa
和bb
動態類型相同(都是A
),動態值也相同(結構體A
,見上面複合類型的比較規則),故二者相等。 aa
和cc
動態類型相同,動態值不一樣,故二者不等。 dd
和ee
動態類型相同(都是*A
),動態值使用指針(引用)類型的比較,因爲不是指向同一個地址,故不等。
注意:
若是接口的動態值不可比較,強行比較會panic
!!!
var a interface{} = []int{1, 2, 3, 4}
var b interface{} = []int{1, 2, 3, 4}
// panic: runtime error: comparing uncomparable type []int
fmt.Println(a == b)
複製代碼
a
和b
的動態值是切片類型,而切片類型不可比較,因此a == b
會panic
。
接口值的比較不要求接口類型(注意不是動態類型)徹底相同,只要一個接口能夠轉化爲另外一個就能夠比較。例如:
var f *os.File
var r io.Reader = f
var rc io.ReadCloser = f
fmt.Println(r == rc) // true
var w io.Writer = f
// invalid operation: r == w (mismatched types io.Reader and io.Writer)
fmt.Println(r == w)
複製代碼
r
的類型爲io.Reader
接口,rc
的類型爲io.ReadCloser
接口。查看源碼,io.ReadCloser
的定義以下:
type ReadCloser interface {
Reader
Closer
}
複製代碼
io.ReadCloser
可轉化爲io.Reader
,故二者可比較。
而io.Writer
不可轉化爲io.Reader
,編譯報錯。
type
定義的類型使用type
能夠基於現有類型定義新的類型。新類型會根據它們的底層類型來比較。例如:
type myint int
var a myint = 10
var b myint = 20
var c myint = 10
fmt.Println(a == b) // false
fmt.Println(a == c) // true
type arr4 [4]int
var aa arr4 = [4]int{1, 2, 3, 4}
var bb arr4 = [4]int{1, 2, 3, 4}
var cc arr4 = [4]int{1, 2, 3, 5}
fmt.Println(aa == bb)
fmt.Println(aa == cc)
複製代碼
myint
根據底層類型int
來比較。 arr4
根據底層類型[4]int
來比較。
前面說過,golang 中的切片類型是不可比較的。全部含有切片的類型都是不可比較的。例如:
不可比較性會傳遞,若是一個結構體因爲含有切片字段不可比較,那麼將它做爲元素的數組不可比較,將它做爲字段類型的結構體不可比較。
map
因爲map
的key
是使用==
來判等的,因此全部不可比較的類型都不能做爲map
的key
。例如:
// invalid map key type []int
m1 := make(map[[]int]int)
type A struct {
a []int
b string
}
// invalid map key type A
m2 := make(map[A]int)
複製代碼
因爲切片類型不可比較,不能做爲map
的key
,編譯時m1 := make(map[[]int]int)
報錯。 因爲結構體A
含有切片字段,不可比較,不能做爲map
的key
,編譯報錯。
本文詳盡介紹了 golang 中==
操做的細節,但願能對你們有所幫助。