1. 程式人生 > 其它 >(轉)Golang reflect.DeepEqual函式:判斷兩個值是否一致

(轉)Golang reflect.DeepEqual函式:判斷兩個值是否一致

介紹:

對於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包含了這麼好的工具。

轉自:https://studygolang.com/articles/2194

參考:https://blog.csdn.net/m0_37710023/article/details/108284171?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.essearch_pc_relevant&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.essearch_pc_relevant