網站首頁 編程語言 正文
前言
平時開發中對比兩個struct
或者map
、slice
是否相等是經常遇到的,有很多對比的方式,比如==
,reflect.DeepEqual()
,cmp.Equal()
等也是經常容易混淆的,這么多種對比方式,適用場景和優缺點都有哪些呢?為什么可以用==
,有的卻不可以呢?問題多多,今天我們來具體總結一下,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
== 的對比方式
== 適用的類型
相信==
判等操作,大家每天都在用。golang
中對==
的處理有一些細節的地方需要特別注意,==
操作最重要的一個前提是:兩個操作數類型必須相同!如果類型不同,那么編譯時就會報錯。
示例代碼:
package main import "fmt" func main() { var a int32 var b int64 // 編譯錯誤:invalid operation a == b (mismatched types int32 and int64) fmt.Println(a == b) }
經常見到使用==
的類型一般是:string
,int
等基本類型。struct
有時候可以用有時候不可以。slice
和map
使用 ==
會報錯。
slice和map使用 ==
因為slice和map不止是需要比較值,還需要比較len和cap,層級比較深的話還需要遞歸比較,不是簡單的==就可以比較的,所以他們各自之間是不可以直接用==比較的,slice和map只能和nil使用==。
- 切片之間不允許比較。切片只能與nil值比較。
- map之間不允許比較。map只能與nil值比較。
s1 := []int64{1, 3} s2 := []int64{1, 2} if s1 == nil {} //編輯器不會提示報錯 if s1 == s2 {} //編輯器會提示報錯
channel使用 ==
channel是引用類型,對比的是存儲數據的地址。channel是可以使用==的,只要類型一樣就可以。
ch1 := make(chan int, 1) ch2 := ch1 if cha2 == cha1{fmt.Println("true")} // true
struct結構體使用==
結構體的定義只是一種內存布局的描述,只有當結構體實例化時,才會真正地分配內存。
實例化就是根據結構體定義的格式創建一份與格式一致的內存區域,結構體實例與實例間的
內存是完全獨立的。對結構體進行&取地址操作時,視為對該類型進行一次 new 的實例化操作
因此:go中的結構體: v = Struct {}, v = &Struct{} 這個兩種寫法是等價的。
- 簡單結構的結構體,里面都是值類型或者指針的話,是可以使用 ==的
- 結構體中含有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") //報錯 } if u1 == u2 { fmt.Println("u1 == u2") } }
reflect.DeepEqual() 和cmp.Equal()
reflect.DeepEqual()
reflect包提供的深度對比(遞歸)的方法,適用于go中的slice,map,struct,function的對比。
對比規則
- 相同類型的值是深度相等的,不同類型的值永遠不會深度相等。
- 當數組值
array
的對應元素深度相等時,數組值是深度相等的。 - 當結構體
struct
值如果其對應的字段(包括導出和未導出的字段)都是深度相等的,則該值是深度相等的。 - 當函數
func
值如果都是零,則是深度相等;否則就不是深度相等。 - 當接口
interface
值如果持有深度相等的具體值,則深度相等。 - 當切片
slice
序號相同,如果值,指針都相等,那么就是深度相等的 - 當哈希表
map
相同的key,如果值,指針都相等,那么就是深度相等的。
通過以上規則可以看到,reflect.DeepEqual
是可以比較struct
的,同時也可以用來比較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 開源的比較庫,它提供了豐富的選項。
對比規則
- 在經過路徑過濾,值過濾和類型過濾之后,會生一些忽略、轉換、比較選項,如果選項中存在忽略,則忽略比較,如果轉換器和比較器的數據大于1,則會panic(因為比較操作不明確)。如果選項中存在轉換器,則調用轉換器轉換當前值,再遞歸調用轉換器輸出類型的Equal。如果包含一個比較器。則比較使用比較器比較當前值。否則進入下一比較階段。
- 如果比較值有一個(T) Equal(T) bool 或者 (T) Equal(I) bool,那么,即使x與y是nil,也會調用x.Equal(y)做為結果。如果不存在這樣的方法,則進入下一階段。
- 在最后階段,Equal方法嘗試比較x與y的基本類型。使用go語言的 == 比較基本類型(bool, intX, float32,float64, complex32,complex64, string, chan)。
- 在比較struct時,將遞歸的比較struct的字段。如果結構體包含未導出的字段,函數會panic可以通過指定cmpopts.IgnoreUnexported來忽略未導出的字段,也可以使用cmp.AllowUnexported來指定比較未導出的字段。
示例代碼:
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的區別
- 安全:cmp.Equal()函數不會比較未導出字段(即字段名首字母小寫的字段)。遇到未導出字段,cmp.Equal()直接panic,reflect.DeepEqual()會比較未導出的字段。
- 強大:cmp.Equal()函數提供豐富的函數參數,讓我們可以實現:忽略部分字段,比較零值,轉換某些值,允許誤差等。
- 共同點:兩種比較類型,都會比較:對象類型,值,指針地址等。切片會按照索引比較值,map是按照key相等比較值。
性能比較
簡單類型的==
對比速度最快
復雜類型,自己知道結構之后寫的自定義對比速度次之
復雜結構且不確定結構的,使用cmp.Equal()或者reflect.DeepEqual()都可以,就是效率低點
assert.Equal()底層使用的就是reflect.DeepEqual()
總結
對于比較兩個struct或者map,slice是否相等,方式很多,效率也有差異。選擇合適自己需求的最重要。相對來說,cmp包是要更安全且可操作性更強一點。
原文鏈接:https://juejin.cn/post/7170332084144177165
相關推薦
- 2022-06-17 一文輕松了解ASP.NET與ASP.NET?Core多環境配置對比_實用技巧
- 2022-10-04 Python基礎之dict和set的使用詳解_python
- 2022-09-28 C語言關于二叉樹中堆的創建和使用整理_C 語言
- 2022-05-12 Scss 遍歷之批量設置樣式
- 2022-11-01 ElasticSearch寫入流程實例解析_相關技巧
- 2022-12-16 python靜態web服務器實現方法及代碼詳解_python
- 2022-10-16 docker保存鏡像到本地并加載本地鏡像文件詳解_docker
- 2022-09-14 Python詳解如何動態給對象增加屬性和方法_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支