相信==
判等操做,你們天天都在用。以前在論壇上看到很多人在問 golang ==
比較的結果。看到不少人對 golang 中==
的結果不太瞭解。確實,golang 中對==
的處理有一些細節的地方須要特別注意。雖然平時可能不太會遇到,可是碰到了就是大坑。本文將對 golang 中==
操做作一個系統的介紹。但願能對你們有所幫助。golang
golang 中的數據類型能夠分爲如下 4 大類:編程
int/uint/int8/uint8/int16/uint16/int32/uint32/int64/uint64/byte/rune
等)、浮點數(float32/float64
)、複數類型(complex64/complex128
)、字符串(string
)。error
。==
操做最重要的一個前提是:兩個操做數類型必須相同!類型必須相同!類型必須相同!數組
若是類型不一樣,那麼編譯時就會報錯。編程語言
注意:ui
C/C++
中的隱式類型轉換。雖然寫起來稍微有些麻煩,可是能避免從此很是多的麻煩!!!type
定義新類型。新定義的類型與底層類型不一樣,不能直接比較。爲了更容易看出類型,示例代碼中的變量定義都顯式指定了類型。設計
看下面的代碼:指針
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) }
沒有隱式類型轉換。code
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,可是他們是不一樣的類型。遞歸
下面依次經過這 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 中==
操做的細節,但願能對你們有所幫助。