Golang判斷struct/slice/map是否相等以及對(duì)比的方法總結(jié)
前言
平時(shí)開發(fā)中對(duì)比兩個(gè)struct
或者map
、slice
是否相等是經(jīng)常遇到的,有很多對(duì)比的方式,比如==
,reflect.DeepEqual()
,cmp.Equal()
等也是經(jīng)常容易混淆的,這么多種對(duì)比方式,適用場(chǎng)景和優(yōu)缺點(diǎn)都有哪些呢?為什么可以用==
,有的卻不可以呢?問題多多,今天我們來(lái)具體總結(jié)一下,感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
== 的對(duì)比方式
== 適用的類型
相信==
判等操作,大家每天都在用。golang
中對(duì)==
的處理有一些細(xì)節(jié)的地方需要特別注意,==
操作最重要的一個(gè)前提是:兩個(gè)操作數(shù)類型必須相同!如果類型不同,那么編譯時(shí)就會(huì)報(bào)錯(cuò)。
示例代碼:
package main import "fmt" func main() { var a int32 var b int64 // 編譯錯(cuò)誤:invalid operation a == b (mismatched types int32 and int64) fmt.Println(a == b) }
經(jīng)常見到使用==
的類型一般是:string
,int
等基本類型。struct
有時(shí)候可以用有時(shí)候不可以。slice
和map
使用 ==
會(huì)報(bào)錯(cuò)。
slice和map使用 ==
因?yàn)閟lice和map不止是需要比較值,還需要比較len和cap,層級(jí)比較深的話還需要遞歸比較,不是簡(jiǎn)單的==就可以比較的,所以他們各自之間是不可以直接用==比較的,slice和map只能和nil使用==。
- 切片之間不允許比較。切片只能與nil值比較。
- map之間不允許比較。map只能與nil值比較。
s1 := []int64{1, 3} s2 := []int64{1, 2} if s1 == nil {} //編輯器不會(huì)提示報(bào)錯(cuò) if s1 == s2 {} //編輯器會(huì)提示報(bào)錯(cuò)
channel使用 ==
channel是引用類型,對(duì)比的是存儲(chǔ)數(shù)據(jù)的地址。channel是可以使用==的,只要類型一樣就可以。
ch1 := make(chan int, 1) ch2 := ch1 if cha2 == cha1{fmt.Println("true")} // true
struct結(jié)構(gòu)體使用==
結(jié)構(gòu)體的定義只是一種內(nèi)存布局的描述,只有當(dāng)結(jié)構(gòu)體實(shí)例化時(shí),才會(huì)真正地分配內(nèi)存。
實(shí)例化就是根據(jù)結(jié)構(gòu)體定義的格式創(chuàng)建一份與格式一致的內(nèi)存區(qū)域,結(jié)構(gòu)體實(shí)例與實(shí)例間的
內(nèi)存是完全獨(dú)立的。對(duì)結(jié)構(gòu)體進(jìn)行&取地址操作時(shí),視為對(duì)該類型進(jìn)行一次 new 的實(shí)例化操作
因此:go中的結(jié)構(gòu)體: v = Struct {}, v = &Struct{} 這個(gè)兩種寫法是等價(jià)的。
- 簡(jiǎn)單結(jié)構(gòu)的結(jié)構(gòu)體,里面都是值類型或者指針的話,是可以使用 ==的
- 結(jié)構(gòu)體中含有slice或者map,都是不可以用==
示例代碼:
package main import ( "fmt" ) type User struct { Name string Age int64 } type People struct { Name string Hobby []string } func main() { p1 := People{Name: "test", Hobby: []string{"唱", "跳"}} p2 := People{Name: "test", Hobby: []string{"唱", "跳"}} u1 := User{Name: "test", Age:18} u2 := User{Name: "test", Age:18} if p1 == p2 { fmt.Println("p1 == p2") //報(bào)錯(cuò) } if u1 == u2 { fmt.Println("u1 == u2") } }
reflect.DeepEqual() 和cmp.Equal()
reflect.DeepEqual()
reflect包提供的深度對(duì)比(遞歸)的方法,適用于go中的slice,map,struct,function的對(duì)比。
對(duì)比規(guī)則
- 相同類型的值是深度相等的,不同類型的值永遠(yuǎn)不會(huì)深度相等。
- 當(dāng)數(shù)組值
array
的對(duì)應(yīng)元素深度相等時(shí),數(shù)組值是深度相等的。 - 當(dāng)結(jié)構(gòu)體
struct
值如果其對(duì)應(yīng)的字段(包括導(dǎo)出和未導(dǎo)出的字段)都是深度相等的,則該值是深度相等的。 - 當(dāng)函數(shù)
func
值如果都是零,則是深度相等;否則就不是深度相等。 - 當(dāng)接口
interface
值如果持有深度相等的具體值,則深度相等。 - 當(dāng)切片
slice
序號(hào)相同,如果值,指針都相等,那么就是深度相等的 - 當(dāng)哈希表
map
相同的key,如果值,指針都相等,那么就是深度相等的。
通過以上規(guī)則可以看到,reflect.DeepEqual
是可以比較struct
的,同時(shí)也可以用來(lái)比較slice
和map
。
示例代碼:
package main import ( "fmt" "reflect" ) type People struct { Name string Hobby []string } func main() { p1 := People{Name: "test", Hobby: []string{"唱", "跳"}} p2 := People{Name: "test", Hobby: []string{"唱", "跳"}} if reflect.DeepEqual(p1, p2) { fmt.Println("struct true") } mp1 := map[int]int{1: 1, 2: 2} mp2 := map[int]int{1: 1, 2: 2} if ok := reflect.DeepEqual(mp1, mp2);ok { fmt.Println("mp1 == mp2!") } else { fmt.Println("mp1 != mp2!") } }
cmp.Equal()
go-cmp是 Google 開源的比較庫(kù),它提供了豐富的選項(xiàng)。
對(duì)比規(guī)則
- 在經(jīng)過路徑過濾,值過濾和類型過濾之后,會(huì)生一些忽略、轉(zhuǎn)換、比較選項(xiàng),如果選項(xiàng)中存在忽略,則忽略比較,如果轉(zhuǎn)換器和比較器的數(shù)據(jù)大于1,則會(huì)panic(因?yàn)楸容^操作不明確)。如果選項(xiàng)中存在轉(zhuǎn)換器,則調(diào)用轉(zhuǎn)換器轉(zhuǎn)換當(dāng)前值,再遞歸調(diào)用轉(zhuǎn)換器輸出類型的Equal。如果包含一個(gè)比較器。則比較使用比較器比較當(dāng)前值。否則進(jìn)入下一比較階段。
- 如果比較值有一個(gè)(T) Equal(T) bool 或者 (T) Equal(I) bool,那么,即使x與y是nil,也會(huì)調(diào)用x.Equal(y)做為結(jié)果。如果不存在這樣的方法,則進(jìn)入下一階段。
- 在最后階段,Equal方法嘗試比較x與y的基本類型。使用go語(yǔ)言的 == 比較基本類型(bool, intX, float32,float64, complex32,complex64, string, chan)。
- 在比較struct時(shí),將遞歸的比較struct的字段。如果結(jié)構(gòu)體包含未導(dǎo)出的字段,函數(shù)會(huì)panic可以通過指定cmpopts.IgnoreUnexported來(lái)忽略未導(dǎo)出的字段,也可以使用cmp.AllowUnexported來(lái)指定比較未導(dǎo)出的字段。
示例代碼:
package main import ( "fmt" "github.com/google/go-cmp/cmp" ) type Contact struct { Phone string Email string } type User struct { Name string Age int Contact *Contact } func main() { u1 := User{Name: "test", Age: 18} u2 := User{Name: "test", Age: 18} fmt.Println("u1 == u2?", u1 == u2) //true fmt.Println("u1 equals u2?", cmp.Equal(u1, u2)) //true c1 := &Contact{Phone: "123456789", Email: "dj@example.com"} c2 := &Contact{Phone: "123456789", Email: "dj@example.com"} u1.Contact = c1 u2.Contact = c1 fmt.Println("u1 == u2 with same pointer?", u1 == u2) //true fmt.Println("u1 equals u2 with same pointer?", cmp.Equal(u1, u2)) //true u2.Contact = c2 fmt.Println("u1 == u2 with different pointer?", u1 == u2) //false fmt.Println("u1 equals u2 with different pointer?", cmp.Equal(u1, u2)) //true }
cmp和DeepEqual的區(qū)別
- 安全:cmp.Equal()函數(shù)不會(huì)比較未導(dǎo)出字段(即字段名首字母小寫的字段)。遇到未導(dǎo)出字段,cmp.Equal()直接panic,reflect.DeepEqual()會(huì)比較未導(dǎo)出的字段。
- 強(qiáng)大:cmp.Equal()函數(shù)提供豐富的函數(shù)參數(shù),讓我們可以實(shí)現(xiàn):忽略部分字段,比較零值,轉(zhuǎn)換某些值,允許誤差等。
- 共同點(diǎn):兩種比較類型,都會(huì)比較:對(duì)象類型,值,指針地址等。切片會(huì)按照索引比較值,map是按照key相等比較值。
性能比較
簡(jiǎn)單類型的==
對(duì)比速度最快
復(fù)雜類型,自己知道結(jié)構(gòu)之后寫的自定義對(duì)比速度次之
復(fù)雜結(jié)構(gòu)且不確定結(jié)構(gòu)的,使用cmp.Equal()或者reflect.DeepEqual()都可以,就是效率低點(diǎn)
assert.Equal()底層使用的就是reflect.DeepEqual()
總結(jié)
對(duì)于比較兩個(gè)struct或者map,slice是否相等,方式很多,效率也有差異。選擇合適自己需求的最重要。相對(duì)來(lái)說,cmp包是要更安全且可操作性更強(qiáng)一點(diǎn)。
到此這篇關(guān)于Golang判斷struct/slice/map是否相等以及對(duì)比的方法總結(jié)的文章就介紹到這了,更多相關(guān)Golang struct slice map內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang中常見的三種并發(fā)控制方式使用小結(jié)
這篇文章主要為大家詳細(xì)介紹了如何對(duì)goroutine并發(fā)行為的控制,在Go中最常見的有三種方式:sync.WaitGroup、channel和Context,下面我們就來(lái)看看他們的具體使用吧2024-01-01GO語(yǔ)言的控制語(yǔ)句詳解包括GO語(yǔ)言的指針語(yǔ)法
這篇文章主要介紹了GO語(yǔ)言的控制語(yǔ)句詳解包括GO語(yǔ)言的指針語(yǔ)法,GO語(yǔ)言switch結(jié)構(gòu),GO語(yǔ)言for的4種結(jié)構(gòu)需要的朋友可以參考下2022-12-12Mango?Cache緩存管理庫(kù)TinyLFU源碼解析
這篇文章主要為大家介紹了Mango?Cache緩存管理庫(kù)TinyLFU源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Golang實(shí)現(xiàn)四種負(fù)載均衡的算法(隨機(jī),輪詢等)
本文介紹了示例介紹了Golang 負(fù)載均衡的四種實(shí)現(xiàn),主要包括了隨機(jī),輪詢,加權(quán)輪詢負(fù)載,一致性hash,感興趣的小伙伴們可以參考一下2021-06-06Golang處理gRPC請(qǐng)求/響應(yīng)元數(shù)據(jù)的示例代碼
前段時(shí)間實(shí)現(xiàn)內(nèi)部gRPC框架時(shí),為了實(shí)現(xiàn)在服務(wù)端攔截器中打印請(qǐng)求及響應(yīng)的頭部信息,便查閱了部分關(guān)于元數(shù)據(jù)的資料,因?yàn)橹形木W(wǎng)絡(luò)上對(duì)于該領(lǐng)域的信息較少,于是在這做了一些簡(jiǎn)單的總結(jié),需要的朋友可以參考下2024-03-03golang類型轉(zhuǎn)換之interface轉(zhuǎn)字符串string簡(jiǎn)單示例
在我們使用Golang進(jìn)行開發(fā)過程中,總是繞不開對(duì)字符或字符串的處理,這篇文章主要給大家介紹了關(guān)于golang類型轉(zhuǎn)換之interface轉(zhuǎn)字符串string的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01使用systemd部署和守護(hù)golang應(yīng)用程序的操作方法
systemd是一個(gè)流行的守護(hù)進(jìn)程管理器,可以輕松管理服務(wù)的啟動(dòng)、停止、重啟等操作,讓我們的應(yīng)用程序始終保持在線,本文介紹了如何使用systemd部署和守護(hù)golang應(yīng)用程序,感興趣的朋友一起看看吧2023-10-10深入解析Go語(yǔ)言中上下文超時(shí)與子進(jìn)程管理
這篇文章小編將通過一個(gè)實(shí)際問題的案例,和大家深入探討一下Go語(yǔ)言中的上下文超時(shí)和子進(jìn)程管理,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-10-10