374

如何检查两个切片是否相等?

4

9 回答 9

293

你应该使用reflect.DeepEqual()

DeepEqual 是 Go 的 == 运算符的递归松弛。

DeepEqual 报告 x 和 y 是否“深度相等”,定义如下。如果以下情况之一适用,则两个相同类型的值非常相等。不同类型的值永远不会完全相等。

当对应的元素深度相等时,数组值深度相等。

如果结构值的对应字段(导出和未导出)深度相等,则结构值深度相等。

如果两者都为零,则 Func 值非常相等;否则他们不是完全平等的。

如果接口值具有高度相等的具体值,则它们是高度相等的。

如果映射值是相同的映射对象,或者它们具有相同的长度并且它们对应的键(使用 Go 相等匹配)映射到深度相等的值,则映射值是深度相等的。

如果指针值使用 Go 的 == 运算符相等或者它们指向深度相等的值,则它们是深度相等的。

当满足以下所有条件时,切片值是完全相等的:它们都是 nil 或都非 nil,它们具有相同的长度,或者它们指向同一底层数组的相同初始条目(即 &x[0 ] == &y[0]) 或其对应的元素(直到长度)非常相等。请注意,非 nil 空切片和 nil 切片(例如,[]byte{} 和 []byte(nil))并不完全相等。

如果使用 Go 的 == 运算符相等,则其他值 - 数字、布尔值、字符串和通道 - 是完全相等的。

于 2013-03-09T15:07:22.440 回答
193

您需要遍历切片中的每个元素并进行测试。未定义切片的相等性。但是,bytes.Equal如果您要比较 type 的值,则有一个函数[]byte

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
}
于 2013-03-09T14:58:16.393 回答
80

这只是使用@VictorDeryagin的答案中给出的reflect.DeepEqual()的示例。

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中尝试

于 2015-10-08T15:46:53.973 回答
41

如果有两个,请使用bytes.Equal[]byte比较它们。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
于 2018-01-05T21:10:39.483 回答
11

现在,这里是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
}
于 2019-07-31T10:25:21.480 回答
5

如果您有兴趣编写测试,那么github.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
于 2019-04-18T18:40:11.300 回答
2

想到一个巧妙的技巧,并认为我会分享。

如果您有兴趣知道两个切片是否相同(即它们为同一数据区域别名)而不是仅仅相等(一个切片的每个索引处的值等于另一个切片的相同索引中的值),那么您可以通过以下方式有效地比较它们:

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)

这种比较有一些注意事项,特别是您不能以这种方式比较空切片,并且切片的容量没有进行比较,因此这种“相同性”属性仅在从切片读取时才真正有用或重新切分严格较窄的子切片,因为任何增加切片的尝试都会受到切片容量的影响。尽管如此,能够有效地声明“这两个巨大的内存块实际上是同一个块,是或否”是非常有用的。

于 2021-08-12T16:18:06.653 回答
0

您不能使用==!=与切片一起使用,但如果您可以将它们与元素一起使用,那么这个问题https://github.com/golang/go/issues/45955显示了一个新功能,可以轻松比较两个切片slices.Equal

// 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 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

slices导入路径为golang.org/x/exp/slices。包下的代码exp实验性的,还不稳定。它可以在 Go 1.19 中升级为标准库。

不过你可以在 Go 1.18 中使用它,https: //go.dev/play/p/QEdoEuYNTlD?v=gotip

    sliceA := []int{1, 2}
    sliceB := []int{1, 2}
    equal := slices.Equal(sliceA, sliceB)
    fmt.Println(equal)

    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)

如果切片的元素不允许==and !=,您可以使用slices.EqualFunc.

于 2022-03-04T10:04:40.840 回答
-1

您可以actual在与排序后的切片进行比较之前对切片进行排序expected

func TestEquality_SomeSlice(t *testing.T) {
    expected := []int{1, 2, 3, 4}
    actual := []int{2, 1, 4, 3}
    
    sort.Slice(actual, func(i, j int) bool {
        return actual[i] < actual[j]
    })

    assert.Equal(t, expected, actual)
}
于 2022-03-05T12:52:06.573 回答