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

背景交代

大家經(jīng)常用"=="來(lái)比較兩個(gè)變量是否相等勇皇。但是golang中的"=="有很多細(xì)節(jié)的地方轰坊,跟php是不一樣的冀宴。很多時(shí)候不能直接用"=="來(lái)比較,編譯器會(huì)直接報(bào)錯(cuò)。

golang中基本類型的比較規(guī)則和復(fù)合類型的不一致聪建,先介紹下golang的變量類型:

  • 1钙畔,基本類型
    • 整型,包括int金麸,uint擎析,int8,uint8挥下,int16揍魂,uint16,int32棚瘟,uint32现斋,int64,uint64偎蘸,byte庄蹋,rune,uintptr等
    • 浮點(diǎn)型迷雪,包括float32限书,float64
    • 復(fù)數(shù)類型,包括complex64振乏,complex128
    • 字符串類型蔗包,string
    • 布爾型,bool
  • 2慧邮,復(fù)合類型
    • 數(shù)組
    • struct結(jié)構(gòu)體
  • 3调限,引用類型
    • slice
    • map
    • channel
    • pointer or 引用類型
  • 4,接口類型
    io.Reader, io.Writer,error等

一误澳,基本類型的變量比較

golang中的基本類型

比較的兩個(gè)變量類型必須相等耻矮。而且,golang沒(méi)有隱式類型轉(zhuǎn)換忆谓,比較的兩個(gè)變量必須類型完全一樣裆装,類型別名也不行。如果要比較倡缠,先做類型轉(zhuǎn)換再比較哨免。

  • 類型完全不一樣的,不能比較
  • 類型再定義關(guān)系昙沦,不能比較琢唾,可以強(qiáng)轉(zhuǎn)比較
  • 類型別名關(guān)系,可以比較
    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

二盾饮,復(fù)合類型的變量比較

復(fù)合類型是逐個(gè)字段采桃,逐個(gè)元素比較的懒熙。需要注意的是,array 或者struct中每個(gè)元素必須要是可比較的普办,如果某個(gè)array的元素 or struct的成員不能比較(比如是后面介紹的slice工扎,map等),則此復(fù)合類型也不能比較衔蹲。

1肢娘,數(shù)組類型變量比較

  • 數(shù)組的長(zhǎng)度是類型的一部分,如果數(shù)組長(zhǎng)度不同踪危,無(wú)法比較
  • 逐個(gè)元素比較類型和值蔬浙。每個(gè)對(duì)應(yīng)元素的比較遵循基本類型變量的比較規(guī)則猪落。跟struct一樣贞远,如果item是不可比較的類型,則array也不能做比較笨忌。

2蓝仲,struct類型變量比較

逐個(gè)成員比較類型和值。每個(gè)對(duì)應(yīng)成員的比較遵循基本類型變量的比較規(guī)則官疲。

    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中有不可比較的成員類型時(shí):

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這種不可比較的成員時(shí),整個(gè)struct都不能做比較途凫,即使沒(méi)有對(duì)slice那個(gè)成員賦值(slice默認(rèn)值為nil)

三垢夹,引用類型的變量比較

slice和map的比較規(guī)則比較奇怪,我們先說(shuō)普通的變量引用類型&val和channel的比較規(guī)則维费。

1果元,普通的變量引用類型&val和channel的比較規(guī)則

引用類型變量存儲(chǔ)的是某個(gè)變量的內(nèi)存地址。所以引用類型變量的比較犀盟,判斷的是這兩個(gè)引用類型存儲(chǔ)的是不是同一個(gè)變量而晒。

  • 如果是同一個(gè)變量,則內(nèi)存地址肯定也一樣阅畴,則引用類型變量相等倡怎,用"=="判斷為true
  • 如果不是同一個(gè)變量,則內(nèi)存地址肯定不一樣贱枣,"=="結(jié)果為false

上面看起來(lái)比較廢話监署,但是得理解引用類型的含義。不然對(duì)判斷規(guī)則還是不清楚纽哥。

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判斷規(guī)則一致
        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)

關(guān)于slice類型不可比較的原因累魔,后面會(huì)專門寫文章做討論。

3够滑, map類型的比較

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

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

接口類型的變量,包含該接口變量存儲(chǔ)的值和值的類型兩部分組成况毅,分別稱為接口的動(dòng)態(tài)類型和動(dòng)態(tài)值分蓖。只有動(dòng)態(tài)類型和動(dòng)態(tài)值都相同時(shí),兩個(gè)接口變量才相同:

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,類型不同
}

而且接口的動(dòng)態(tài)類型必須要是可比較的尔许,如果不能比較(比如slice么鹤,map),則運(yùn)行時(shí)會(huì)報(bào)panic味廊。因?yàn)榫幾g器在編譯時(shí)無(wú)法獲取接口的動(dòng)態(tài)類型蒸甜,所以編譯能通過(guò),但是運(yùn)行時(shí)直接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

}

五余佛,函數(shù)類型的比較

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

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

六辉巡,slice和map的特殊比較

上面說(shuō)過(guò)恨憎,map和slice是不可比較類型,但是有沒(méi)有特殊的方法來(lái)對(duì)slice和map做比較呢郊楣,有

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

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函數(shù)可以用來(lái)比較兩個(gè)任意類型的變量

func DeepEqual(x, y interface{})

對(duì)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

對(duì)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

對(duì)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
}

可以發(fā)現(xiàn)喇嘱,只要變量的類型和值相同的話,reflect.DeepEqual比較的結(jié)果就為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)
    }
}

結(jié)果為:

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

五者铜,總結(jié)

  • 1,復(fù)合類型放椰,只有每個(gè)元素(成員)可比較作烟,而且類型和值都相等時(shí),兩個(gè)復(fù)合元素才相等
  • 2砾医,slice拿撩,map不可比較,但是可以用reflect或者cmp包來(lái)比較
  • 3如蚜,func作為golnag的一等公民压恒,也是一個(gè)類型影暴,也不能比較。
  • 4探赫,引用類型的比較是看指向的是不是同一個(gè)變量
  • 5型宙,類型再定義(type A string)不可比較,是兩種不同的類型
  • 6伦吠,類型別名(type A = string)可比較妆兑,是同一種類型。

六毛仪,拓展知識(shí)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屉来,一起剝皮案震驚了整個(gè)濱河市路翻,隨后出現(xiàn)的幾起案子狈癞,更是在濱河造成了極大的恐慌,老刑警劉巖茂契,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝶桶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡掉冶,警方通過(guò)查閱死者的電腦和手機(jī)真竖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)厌小,“玉大人恢共,你說(shuō)我怎么就攤上這事¤笛牵” “怎么了讨韭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)癣蟋。 經(jīng)常有香客問(wèn)我透硝,道長(zhǎng),這世上最難降的妖魔是什么疯搅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任濒生,我火速辦了婚禮,結(jié)果婚禮上幔欧,老公的妹妹穿的比我還像新娘罪治。我一直安慰自己丽声,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布觉义。 她就那樣靜靜地躺著恒序,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谁撼。 梳的紋絲不亂的頭發(fā)上歧胁,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音厉碟,去河邊找鬼喊巍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛箍鼓,可吹牛的內(nèi)容都是我干的崭参。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼款咖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼何暮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起铐殃,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤海洼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后富腊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體坏逢,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年赘被,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了是整。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡民假,死狀恐怖浮入,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情羊异,我是刑警寧澤事秀,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站球化,受9級(jí)特大地震影響秽晚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜筒愚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一赴蝇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巢掺,春花似錦句伶、人聲如沸劲蜻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)先嬉。三九已至,卻和暖如春楚堤,著一層夾襖步出監(jiān)牢的瞬間疫蔓,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工身冬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衅胀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓酥筝,卻偏偏與公主長(zhǎng)得像滚躯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嘿歌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361