如果运算符==和!=不是一个选项,我如何检查两个切片是否相等?

package main

import "fmt"

func main() {
    s1 := []int{1, 2}
    s2 := []int{1, 2}
    fmt.Println(s1 == s2)
}

它不使用:

无效的操作:s1 == s2 (slice只能与nil进行比较)


当前回答

你不能对切片使用==或!=,但如果你可以对元素使用==或!=,那么Go 1.18有一个新功能,可以轻松地比较两个切片。平等:

Equal报告两个切片是否相等:长度相同且所有元素相等。如果长度不同,Equal返回false。否则,元素按照递增的索引顺序进行比较,并且比较在第一个不相等的对处停止。浮点nan不被认为是相等的。

切片包的导入路径为golang.org/x/exp/slices。exp包中的代码是实验性的,还不稳定。它最终将被移到Go 1.19的标准库中。

尽管如此,你可以在Go 1.18(游乐场)中使用它

    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

如果片的元素不允许==和!=,您可以使用片。EqualFunc并定义任何对元素类型有意义的比较器函数。

其他回答

想到了一个妙招,想和大家分享一下。

如果你感兴趣的是两个切片是否相同(即它们在相同的数据区域中使用别名),而不仅仅是相等(一个切片的每个索引上的值等于另一个切片的相同索引上的值),那么你可以用以下方式有效地比较它们:

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)

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

如果你有两个[]字节,使用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

如果您对编写测试感兴趣,那么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

你应该使用reflect.DeepEqual()

DeepEqual is a recursive relaxation of Go's == operator. DeepEqual reports whether x and y are “deeply equal,” defined as follows. Two values of identical type are deeply equal if one of the following cases applies. Values of distinct types are never deeply equal. Array values are deeply equal when their corresponding elements are deeply equal. Struct values are deeply equal if their corresponding fields, both exported and unexported, are deeply equal. Func values are deeply equal if both are nil; otherwise they are not deeply equal. Interface values are deeply equal if they hold deeply equal concrete values. Map values are deeply equal if they are the same map object or if they have the same length and their corresponding keys (matched using Go equality) map to deeply equal values. Pointer values are deeply equal if they are equal using Go's == operator or if they point to deeply equal values. Slice values are deeply equal when all of the following are true: they are both nil or both non-nil, they have the same length, and either they point to the same initial entry of the same underlying array (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal. Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal. Other values - numbers, bools, strings, and channels - are deeply equal if they are equal using Go's == operator.

现在,这里是https://github.com/google/go-cmp

是一种更强大、更安全的反映方式。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
}