這可能是最全的golang的"=="比較規則了吧

背景交代

大家經常用"=="來比較兩個變量是否相等。但是golang中的"=="有很多細節的地方,跟php是不一樣的。很多時候不能直接用"=="來比較,編譯器會直接報錯。

golang中基本類型的比較規則和復合類型的不一致,先介紹下golang的變量類型:

  • 1,基本類型
    • 整型,包括int,uint,int8,uint8,int16,uint16,int32,uint32,int64,uint64,byte,rune,uintptr等
    • 浮點型,包括float32,float64
    • 復數類型,包括complex64,complex128
    • 字符串類型,string
    • 布爾型,bool
  • 2,復合類型
    • 數組
    • struct結構體
  • 3,引用類型
    • slice
    • map
    • channel
    • pointer or 引用類型
  • 4,接口類型
    io.Reader, io.Writer,error等

一,基本類型的變量比較

golang中的基本類型

比較的兩個變量類型必須相等。而且,golang沒有隱式類型轉換,比較的兩個變量必須類型完全一樣,類型別名也不行。如果要比較,先做類型轉換再比較。

  • 類型完全不一樣的,不能比較
  • 類型再定義關系,不能比較,可以強轉比較
  • 類型別名關系,可以比較
    fmt.Println("2" == 2) //invalid operation: "2" == 2 (mismatched types string and int)

    type A int
    var a int = 1
    var b A = 1
    fmt.Println(a == b) //invalid operation: a == b (mismatched types int and A)
    fmt.Println(a == int(b)) //true

    type C = int
    var c C = 1
    fmt.Println(a == c) //true

二,復合類型的變量比較

復合類型是逐個字段,逐個元素比較的。需要注意的是,array 或者struct中每個元素必須要是可比較的,如果某個array的元素 or struct的成員不能比較(比如是后面介紹的slice,map等),則此復合類型也不能比較。

1,數組類型變量比較

  • 數組的長度是類型的一部分,如果數組長度不同,無法比較
  • 逐個元素比較類型和值。每個對應元素的比較遵循基本類型變量的比較規則。跟struct一樣,如果item是不可比較的類型,則array也不能做比較。

2,struct類型變量比較

逐個成員比較類型和值。每個對應成員的比較遵循基本類型變量的比較規則。

    type Student struct {
        Name string
        Age  int
    }

    a := Student{"minping", 30}
    b := Student{"minping", 30}
    fmt.Println(a == b)   //true
    fmt.Println(&a == &b) //false

但是如果struct中有不可比較的成員類型時:

type Student struct {
        Name string
        Age  int
        Info []string
    }

    a := Student{
        Name: "minping",
        Age:  30,
    }
    b := Student{
        Name: "minping",
        Age:  30,
    }
    fmt.Println(a == b)   //invalid operation: a == b (struct containing []string cannot be compared)

可以看到,struct中有slice這種不可比較的成員時,整個struct都不能做比較,即使沒有對slice那個成員賦值(slice默認值為nil)

三,引用類型的變量比較

slice和map的比較規則比較奇怪,我們先說普通的變量引用類型&val和channel的比較規則。

1,普通的變量引用類型&val和channel的比較規則

引用類型變量存儲的是某個變量的內存地址。所以引用類型變量的比較,判斷的是這兩個引用類型存儲的是不是同一個變量。

  • 如果是同一個變量,則內存地址肯定也一樣,則引用類型變量相等,用"=="判斷為true
  • 如果不是同一個變量,則內存地址肯定不一樣,"=="結果為false

上面看起來比較廢話,但是得理解引用類型的含義。不然對判斷規則還是不清楚。

type Student struct {
        Name string
        Age  int
    }

    a := &Student{"minping", 30}

    b := &Student{"minping", 30}
    fmt.Println(a == b) //false

    c := a
    fmt.Println(a == c) //true
        
        //作為引用類型,channel和普通的&val判斷規則一致
        ch1 := make(chan int, 1)
        ch2 := make(chan int, 1)
        ch3 := ch1

        fmt.Println(ch1 == ch2) //false
        fmt.Println(ch1 == ch3) //true

2,slice這種引用類型的比較

slice類型不可比較,只能與零值nil做比較。

    a := []string{}
    b := []string{}
    fmt.Println(a == b)  //invalid operation: a == b (slice can only be compared to nil)

關于slice類型不可比較的原因,后面會專門寫文章做討論。

3, map類型的比較

map類型和slice一樣,不能比較,只能與nil做比較。

四,interface{}類型變量的比較

接口類型的變量,包含該接口變量存儲的值和值的類型兩部分組成,分別稱為接口的動態類型和動態值。只有動態類型和動態值都相同時,兩個接口變量才相同:

type Person interface {
    getName() string
}

type Student struct {
    Name string
}

type Teacher struct {
    Name string
}

func (s Student) getName() string {
    return s.Name
}

func (t Teacher) getName() string {
    return t.Name
}

func compare(s, t Person) bool {
    return s == t
}

func main() {

    s1 := Student{"minping"}
    s2 := Student{"minping"}
    t := Teacher{"minping"}

    fmt.Println(compare(s1, s2)) //true
    fmt.Println(compare(s1, t))  //false,類型不同
}

而且接口的動態類型必須要是可比較的,如果不能比較(比如slice,map),則運行時會報panic。因為編譯器在編譯時無法獲取接口的動態類型,所以編譯能通過,但是運行時直接panic:

type Person interface {
    getName() string
}

type Student map[string]string

type Teacher map[string]string

func (s Student) getName() string {
    return s["name"]
}

func (t Teacher) getName() string {
    return t["name"]
}

func compare(s, t Person) bool {
    return s == t
}

func main() {

    s1 := Student{}
    s1["name"] = "minping"
    s2 := Student{}
    s2["name"] = "minping"

    fmt.Println(compare(s1, s2)) //runtime error: comparing uncomparable type main.Student

}

五,函數類型的比較

golang的func作為一等公民,也是一種類型,而且不可比較

f := func(int) int { return 1 }
g := func(int) int { return 2 }
f == g

六,slice和map的特殊比較

上面說過,map和slice是不可比較類型,但是有沒有特殊的方法來對slice和map做比較呢,有

1,[]byte類型的變量,使用工具包byte提供的函數就可以做比較

s1 := []byte{'f', 'o', 'o'}
s2 := []byte{'f', 'o', 'o'}
fmt.Println(bytes.Equal(s1, s2)) // true
s2 = []byte{'b', 'a', 'r'}
fmt.Println(bytes.Equal(s1, s2)) // false
s2 = []byte{'f', 'O', 'O'}
fmt.Println(bytes.EqualFold(s1, s2)) // true
s1 = []byte("?d?b?o")
s2 = []byte("?d?b?O")
fmt.Println(bytes.EqualFold(s1, s2)) // true
s1 = []byte{}
s2 = nil
fmt.Println(bytes.Equal(s1, s2)) // true

2,使用反射

reflect.DeepEqual函數可以用來比較兩個任意類型的變量

func DeepEqual(x, y interface{})

對map類型做比較:

m1 := map[string]int{"foo": 1, "bar": 2}
m2 := map[string]int{"foo": 1, "bar": 2}
// fmt.Println(m1 == m2) // map can only be compared to nil
fmt.Println(reflect.DeepEqual(m1, m2)) // true
m2 = map[string]int{"foo": 1, "bar": 3}
fmt.Println(reflect.DeepEqual(m1, m2)) // false
m3 := map[string]interface{}{"foo": [2]int{1,2}}
m4 := map[string]interface{}{"foo": [2]int{1,2}}
fmt.Println(reflect.DeepEqual(m3, m4)) // true
var m5 map[float64]string
fmt.Println(reflect.DeepEqual(m5, nil)) // false
fmt.Println(m5 == nil) // true

對slice類型做比較:

s := []string{"foo"}
fmt.Println(reflect.DeepEqual(s, []string{"foo"})) // true
fmt.Println(reflect.DeepEqual(s, []string{"bar"})) // false
s = nil
fmt.Println(reflect.DeepEqual(s, []string{})) // false
s = []string{}
fmt.Println(reflect.DeepEqual(s, []string{})) // true

對struct類型做比較:

type T struct {
    name string
    Age  int
}
func main() {
    t := T{"foo", 10}
    fmt.Println(reflect.DeepEqual(t, T{"bar", 20})) // false
    fmt.Println(reflect.DeepEqual(t, T{"bar", 10})) // false
    fmt.Println(reflect.DeepEqual(t, T{"foo", 10})) // true
}

可以發現,只要變量的類型和值相同的話,reflect.DeepEqual比較的結果就為true

2,使用google的cmp包

直接看用例:

import (
    "fmt"
    "github.com/google/go-cmp/cmp"
)
type T struct {
    Name string
    Age  int
    City string
}
func main() {
    x := T{"Micha?", 99, "London"}
    y := T{"Adam", 88, "London"}
    if diff := cmp.Diff(x, y); diff != "" {
        fmt.Println(diff)
    }
}

結果為:

 main.T{
-       Name: "Micha?",
+       Name: "Adam",
-       Age:  99,
+       Age:  88,
        City: "London",
  }

五,總結

  • 1,復合類型,只有每個元素(成員)可比較,而且類型和值都相等時,兩個復合元素才相等
  • 2,slice,map不可比較,但是可以用reflect或者cmp包來比較
  • 3,func作為golnag的一等公民,也是一個類型,也不能比較。
  • 4,引用類型的比較是看指向的是不是同一個變量
  • 5,類型再定義(type A string)不可比較,是兩種不同的類型
  • 6,類型別名(type A = string)可比較,是同一種類型。

六,拓展知識

1, golang的類型再定義和類型別名
2,golang的slice和map為什么不可以比較

七,參考

1,https://medium.com/golangspec/equality-in-golang-ff44da79b7f1

2,https://studygolang.com/articles/19144
3,https://juejin.im/post/5d5ff27d518825637965f3f3

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374