在 Golang 中,要判斷兩個 slice 是否相等是不能直接使用 == 運算符的(== 只能說明 兩個 slice 是否指向同一個底層數組)。如果兩個 slice 的底層數組相同,但長度或容量不同,則仍然不相等;反之,如果兩個 slice 的底層數組不同,即使有相同的元素也不相等。因此,為了判斷兩個 slice 是否相等,需要比較它們的元素。
循環遍歷比較
下面是一種比較 slice 是否相等的通用方法,需要遍歷兩個 slice 內的元素并進行逐個比較:
package main
func equalWithLoop(a, b []int) bool {
if len(a) != len(b) { // 長度不同,直接返回 false
return false
}
if (a == nil) != (b == nil) {
return false
}
for i, v := range a { // 每個元素逐一比較
if v != b[i] {
return false
}
}
return true
}
首先,通過 len 函數比較長度,如果長度不同,那么肯定不相等,直接返回 false。然后,把 []int{} != []int(nil) 這種情況也考慮進去。最后使用 for 循環逐一比較每個元素,如果發現有不相等的元素,返回 false,如果遍歷結束后沒有發現不相等的元素,返回 true。
需要注意的是,這種方法只適用于切片元素是可比較類型的情況。如果切片元素是結構體等不可比較類型,則需要使用 reflect.DeepEqual 函數來比較兩個切片是否相等。
reflect比較
可以使用 reflect.DeepEqual() 函數來進行比較,這個函數會遞歸比較兩個 slice 中的元素,如果完全相同,則返回true,否則返回false。示例代碼如下:
package main
import "reflect"
func equalWithReflect(a, b []int) bool {
return reflect.DeepEqual(a, b)
}
這種方法雖然很簡潔,但是由于使用了反射和遞歸,效率比較低。
兩種方法效率對比
接下來使用Benchmark來簡單測試下兩種方法的效率,編寫測試代碼如下:
package main
import "testing"
func BenchmarkEqualWithLoop(b *testing.B) {
sa := []int{1, 3, 5, 7, 9}
sb := []int{2, 4, 6, 8, 0}
b.ResetTimer()
for n := 0; n < b.N; n++ {
equalWithLoop(sa, sb)
}
}
func BenchmarkEqualWithReflect(b *testing.B) {
sa := []int{1, 3, 5, 7, 9}
sb := []int{2, 4, 6, 8, 0}
b.ResetTimer()
for n := 0; n < b.N; n++ {
equalWithReflect(sa, sb)
}
}
在測試文件所在目錄運行如下命令
go test -bench=.
在我電腦上運行結果如下
luduoxin$ go test -bench=.
goos: darwin
goarch: amd64
pkg: test/cmp
cpu: Intel(R) Core(TM) i5-8259U CPU @ 2.30GHz
BenchmarkEqualWithLoop-8 1000000000 0.8794 ns/op
BenchmarkEqualWithReflect-8 6162374 196.2 ns/op
PASS
ok hello/slice 2.847s
可以看出reflect方式要慢上幾個數量級。