介紹:
對於array、slice、map、struct等類型,想要比較兩個值是否相等,不能使用==,處理起來十分麻煩,在對效率沒有太大要求的情況下,reflect包中的DeepEqual函數完美的解決了比較問題。
函數簽名:
func DeepEqual(a1, a2 interface{}) bool
文檔中對該函數的說明:
DeepEqual函數用來判斷兩個值是否深度一致:除了類型相同;在可以時(主要是基本類型)會使用==;但還會比較array、slice的成員,map的鍵值對,結構體字段進行深入比對。map的鍵值對,對鍵只使用==,但值會繼續往深層比對。DeepEqual函數可以正確處理循環的類型。函數類型只有都會nil時才相等;空切片不等於nil切片;還會考慮array、slice的長度、map鍵值對數。
示例:
如果有兩個map,內容都一樣,只有順序不同
m1:=map[string]int{"a":1,"b":2,"c":3}; m2:=map[string]int{"a":1,"c":3,"b":2};
我們怎么判斷二者是否一致呢?
如果你打算這么寫:
fmt.Println("m1==m2",m1==m2)
這是行不通的,go沒有重寫map的==操作符,編譯器會報告錯誤:
invalid operation: m1 == m2 (map can only be compared to nil)
意思是map的變量只能和空(nil)比較,例如:
fmt.Println("m1 == nil?",m1==nil) fmt.Println("m2 != nil?",m2!=nil)
這沒有問題,執行結果是:
Running...
m1 == nil? false
m2 != nil? true
那怎么比較呢?如果要編程實現,還真是麻煩,比如我的想法是:循環m1,看看每個key是否都在m2中存在,再比較m1[key]是否和m2[key]相等,如果都ok,再依次循環m2。還真是麻煩:
func cmpMap(m1,m2 map[string]int)bool{ for k1,v1 :=range m1{ if v2,has:=m2[k1];has{ if v1!=v2 { return false } }else{ return false; } } for k2,v2:=range m2{ if v1,has:=m1[k2];has{ if v1!=v2{ return false; } }else{ return false; } } return true; }
其實,go的反射包中有一個巨好用的武器reflect.DeepEqual,可以方便解決這個問題,請看:
package main import( "fmt" "reflect" ) type tt struct{ Code int } func main(){ m1:=map[string]int{"a":1,"b":2,"c":3}; m2:=map[string]int{"a":1,"c":3,"b":2}; fmt.Println("m1 == nil?",m1==nil) fmt.Println("m2 != nil?",m2!=nil) //fmt.Println("m1==m2",m1==m2) fmt.Println("cmpMap(m1,m2) = ",cmpMap(m1,m2)); fmt.Println("reflect.DeepEqual(m1,m2) = ",reflect.DeepEqual(m1,m2)) fmt.Println() m3:=map[string]int{"a":1,"b":2,"c":3,"d":1}; fmt.Println("cmpMap(m1,m3)=",cmpMap(m1,m3)); fmt.Println("reflect.DeepEqual(m1,m3) = ",reflect.DeepEqual(m1,m3)) } func cmpMap(m1,m2 map[string]int)bool{ for k1,v1 :=range m1{ if v2,has:=m2[k1];has{ if v1!=v2 { return false } }else{ return false; } } for k2,v2:=range m2{ if v1,has:=m1[k2];has{ if v1!=v2{ return false; } }else{ return false; } } return true; }
執行結果:
Running... m1 == nil? false m2 != nil? true cmpMap(m1,m2) = true reflect.DeepEqual(m1,m2) = true cmpMap(m1,m3)= false reflect.DeepEqual(m1,m3) = false Success: process exited with code 0.
但是美中不足的是,由於reflect.DeepEqual需要經過反射操作,效率比我們自己寫的函數差的多了,寫個簡單的測試:
start:=time.Now(); for i:=0;i<100000;i++{ cmpMap(m1,m2) } end:=time.Now(); du:=end.Sub(start) fmt.Println("100000 call cmpMap(m1,m2) elapsed=",du) start=time.Now(); for i:=0;i<100000;i++{ reflect.DeepEqual(m1,m2); } end=time.Now(); du=end.Sub(start); fmt.Println("100000 call reflect.DeepEqual(m1,m2) elapsed=",du)
看看結果,大約有10倍的差距
100000 call cmpMap(m1,m2) elapsed= 75.544992ms 100000 call reflect.DeepEqual(m1,m2) elapsed= 735.577069ms
當然,在一般情況下,這點兒性能損失不算什么,尤其在不確定類型需要反射的時候,更是我們不可不用的強大工具。
比如:
func main(){ m1:=map[string]interface{}{"a":"1", "b":2, "c":3}; m2:=map[string]interface{}{"a":1, "c":"3", "b":2}; fmt.Println(`reflect.DeepEqual(m1["a"],m2["a"]`,reflect.DeepEqual(m1["a"],m2["a"])); fmt.Println(`reflect.DeepEqual(m1["b"],m2["b"]`,reflect.DeepEqual(m1["b"],m2["b"])); }
執行結果:
Running... reflect.DeepEqual(m1["a"],m2["a"] false reflect.DeepEqual(m1["b"],m2["b"] true
這種情況,如果我們自己寫代碼比較,勢必要使用switch type語法,實在是太麻煩了,感謝go包含了這么好的工具。