如果==
和!=
不可用,我该如何检查两个切片是否相等?
package main
import "fmt"
func main() {
s1 := []int{1, 2}
s2 := []int{1, 2}
fmt.Println(s1 == s2)
}
这无法编译,显示错误信息:
无效操作:s1 == s2(切片只能与nil进行比较)
如果==
和!=
不可用,我该如何检查两个切片是否相等?
package main
import "fmt"
func main() {
s1 := []int{1, 2}
s2 := []int{1, 2}
fmt.Println(s1 == s2)
}
这无法编译,显示错误信息:
无效操作:s1 == s2(切片只能与nil进行比较)
你应该使用reflect.DeepEqual()
DeepEqual是Go语言==运算符的递归放松版本。
DeepEqual报告x和y是否“深度相等”,定义如下。 如果以下情况之一适用,则具有相同类型的两个值深度相等。 不同类型的值永远不会深度相等。
数组值在其对应元素深度相等时深度相等。
结构体值如果其对应的字段(包括公开和未公开的字段)深度相等,则深度相等。
函数值如果都为nil则它们深度相等;否则它们不深度相等。
接口值如果它们持有深度相等的具体值,则它们深度相等。
Map值如果它们是相同的映射对象或者如果它们具有相同的长度并且其相应键(使用Go相等性匹配)映射到深度相等的值,则它们深度相等。
指针值如果使用Go的==运算符相等或者它们指向深度相等的值,则它们深度相等。
切片值在以下所有条件均为真时深度相等:它们都是nil或都是非nil,它们具有相同的长度,并且它们指向相同底层数组的相同初始条目(即&x[0] == &y[0]),或者它们的相应元素(长度最多)是深度相等的。请注意,一个非nil的空切片和一个nil切片(例如[]byte{}和[]byte(nil))不是深度相等的。
其他值-数字、布尔值、字符串和通道-如果使用Go的==运算符相等,则它们深度相等。
您需要循环遍历切片中的每个元素并进行测试。对于切片,等式是没有定义的。然而,如果您正在比较[]byte
类型的值,则有一个bytes.Equal
函数可用。
func testEq(a, b []Type) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
for i, v := range a { if v != b[i] { return false } }
。意思是对于两个切片a和b,通过循环比较它们的每个元素是否相等来检查它们是否相等。如果找到不相等的元素,则返回false表示它们不相等。 - zzzznil
切片等于长度为0的切片,因为我的切片是如何创建/初始化的可能会导致不同的结果。例如,使用短变量声明通常会写成s:= []string{}
,它将不是nil
,但使用var
它将是var s []string
,这将将其初始化为nil
。请注意,两者都打印为空切片[]
:[Go Playground] (http://play.golang.org/p/MJYiSq1q39) - icza这只是一个例子,使用了reflect.DeepEqual(),该函数在@VictorDeryagin的回答中提到。
package main
import (
"fmt"
"reflect"
)
func main() {
a := []int {4,5,6}
b := []int {4,5,6}
c := []int {4,5,6,7}
fmt.Println(reflect.DeepEqual(a, b))
fmt.Println(reflect.DeepEqual(a, c))
}
结果:
true
false
在 Go Playground 中尝试一下
如果你有两个[]byte
,可以使用bytes.Equal进行比较。Golang文档表示:
Equal返回一个布尔值,指示a和b是否具有相同的长度并包含相同的字节。 nil参数等效于空切片。
用法:
package main
import (
"fmt"
"bytes"
)
func main() {
a := []byte {1,2,3}
b := []byte {1,2,3}
c := []byte {1,2,2}
fmt.Println(bytes.Equal(a, b))
fmt.Println(bytes.Equal(a, c))
}
这将被打印
true
false
目前,这里有一个https://github.com/google/go-cmp,它旨在成为比reflect.DeepEqual
更强大、更安全的替代品,用于比较两个值是否语义相等。
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
func main() {
a := []byte{1, 2, 3}
b := []byte{1, 2, 3}
fmt.Println(cmp.Equal(a, b)) // true
}
当使用切片时,不能使用==
或!=
进行比较,但如果可以对元素进行比较,则Go 1.18提供了一个新函数来轻松比较两个切片:slices.Equal
:
Equal
用于报告两个切片是否相等:长度相同且所有元素都相等。如果长度不同,则返回false
。否则,按递增索引顺序比较元素,并在第一组不相等的元素处停止比较。浮点NaN不被视为相等。
slices
包的导入路径是golang.org/x/exp/slices。exp
包中的代码是实验性的,尚未稳定。它最终将会被移动到标准库中 在Go 1.19中。
然而,您可以在Go 1.18及之后的版本中立即使用它 (playground)。
sliceA := []int{1, 2}
sliceB := []int{1, 2}
equal := slices.Equal(sliceA, sliceB)
fmt.Println(equal) // true
type data struct {
num float64
label string
}
sliceC := []data{{10.99, "toy"}, {500.49, "phone"}}
sliceD := []data{{10.99, "toy"}, {200.0, "phone"}}
equal = slices.Equal(sliceC, sliceD)
fmt.Println(equal) // true
如果切片的元素不支持==
和!=
,您可以使用slices.EqualFunc
,并为元素类型定义任何有意义的比较函数。"golang.org/x/exp/slices"
。 - Ericgithub.com/stretchr/testify/assert
将是您的好朋友。
在文件开头导入该库:import (
"github.com/stretchr/testify/assert"
)
然后在测试内部执行:
func TestEquality_SomeSlice (t * testing.T) {
a := []int{1, 2}
b := []int{2, 1}
assert.Equal(t, a, b)
}
Diff:
--- Expected
+++ Actual
@@ -1,4 +1,4 @@
([]int) (len=2) {
+ (int) 1,
(int) 2,
- (int) 2,
(int) 1,
Test: TestEquality_SomeSlice
assert.Equal
内部使用 reflect.DeepEqual
,这可能会使您的测试运行变慢,并最终影响您的流水线。 - Deepak Sahassert.ElementsMatch(t, a, b)
来忽略元素顺序。 - marcelosalloum想到了一个巧妙的技巧,想和大家分享。
如果你想知道的是两个切片是否相同(即它们别名为数据的同一区域),而不仅仅是相等(一个切片中每个索引处的值等于另一个切片中相同索引处的值),那么你可以通过以下方式高效比较它们:
foo := []int{1,3,5,7,9,11,13,15,17,19}
// these two slices are exactly identical
subslice1 := foo[3:][:4]
subslice2 := foo[:7][3:]
slicesEqual := &subslice1[0] == &subslice2[0] &&
len(subslice1) == len(subslice2)
在进行这种比较时需要注意一些限制,特别是不能以此方式比较空切片,并且不会比较切片的容量,因此这种“相同性”属性仅在从切片中读取或重新切割严格更窄的子切片时才真正有用,因为任何尝试增长切片的操作都会受到切片容量的影响。尽管如此,能够高效地声明“这两个巨大的内存块实际上是相同的块,是或否”仍然非常有用。
fmt.Printf("%p %p\n", &subslice1[0], &subslice2[0])
来查看两者是否共享相同的内存地址,并且只要将相同的索引与两个切片进行比较,它就可以正常工作。例如 fmt.Printf("%p %p\n", &subslice1[1], &subslice2[1])
等。 - user4466350package main
import (
"fmt"
"slices"
)
func main() {
s1 := []int{1, 4, 1, 4, 2, 1, 3, 5, 6, 2}
s2 := []int{1, 4, 1, 4, 2, 1, 3, 5, 6, 2}
fmt.Println(slices.Equal(s1, s2))
s3 := []string{"foo", "bar"}
s4 := []string{"foo", "baz"}
fmt.Println(slices.Equal(s3, s4))
}
https://cs.opensource.google/go/x/exp/+/06a737ee:slices/slices.go;l=22
// Equal reports whether two slices are equal: the same length and all
// elements equal. If the lengths are different, Equal returns false.
// Otherwise, the elements are compared in increasing index order, and the
// comparison stops at the first unequal pair.
// Floating point NaNs are not considered equal.
func Equal[E comparable](s1, s2 []E) bool {
if len(s1) != len(s2) {
return false
}
for i := range s1 {
if s1[i] != s2[i] {
return false
}
}
return true
}
package main
import (
"fmt"
"golang.org/x/exp/slices"
)
func main() {
s1 := []int{1, 2}
s2 := []int{1, 2}
equal := slices.Equal(s1, s2)
fmt.Println("Is Equal ? ", equal)
}