检查两个切片的相等性

huangapple go评论93阅读模式
英文:

Checking the equality of two slices

问题

如何检查两个切片是否相等,假设操作符==!=不可用?

package main

import "fmt"

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

func isEqual(s1, s2 []int) bool {
	if len(s1) != len(s2) {
		return false
	}
	for i := range s1 {
		if s1[i] != s2[i] {
			return false
		}
	}
	return true
}

这段代码无法编译通过:

invalid operation: s1 == s2 (slice can only be compared to nil)

英文:

How can I check if two slices are equal, given that the operators == and != are not an option?

package main

import "fmt"

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

This does not compile with:

> invalid operation: s1 == s2 (slice can only be compared to nil)

答案1

得分: 333

你应该使用reflect.DeepEqual()

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

DeepEqual报告x和y是否“深度相等”,定义如下。如果满足以下任一情况,则具有相同类型的两个值是深度相等的。不同类型的值永远不会深度相等。

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

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

如果两个函数值都为nil,则它们是深度相等的;否则它们不是深度相等的。

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

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

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

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

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

英文:

You should use 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.

答案2

得分: 218

你需要循环遍历切片中的每个元素并进行测试。切片的相等性没有定义。然而,如果你要比较[]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
}
英文:

You need to loop over each of the elements in the slice and test. Equality for slices is not defined. However, there is a bytes.Equal function if you are comparing values of 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
}

答案3

得分: 99

这只是一个使用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中尝试一下

英文:

This is just example using reflect.DeepEqual() that is given in @VictorDeryagin's answer.

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

}

Result:

true
false

Try it in Go Playground

答案4

得分: 57

如果你有两个[]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
英文:

If you have two []byte, compare them using bytes.Equal. The Golang documentation says:

> Equal returns a boolean reporting whether a and b are the same length and contain the same bytes. A nil argument is equivalent to an empty slice.

Usage:

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))
}

This will print

true
false

答案5

得分: 17

现在,这里有一个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
}
英文:

And for now, here is https://github.com/google/go-cmp which

> is intended to be a more powerful and safer alternative to reflect.DeepEqual for comparing whether two values are semantically equal.

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
}

答案6

得分: 14

你不能使用==!=来比较切片,但是如果你可以使用它们来比较元素,那么Go 1.18有一个新的函数可以轻松比较两个切片,slices.Equal

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

slices包的导入路径是golang.org/x/exp/slicesexp包中的代码是实验性的,尚未稳定。它最终将被移入标准库<strike>在Go 1.19中</strike>。

尽管如此,你可以在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并定义适合元素类型的比较函数。

英文:

You cannot use == or != with slices but if you can use them with the elements then Go 1.18 has a new function to easily compare two slices, 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 increasing index order, and the comparison stops at the first unequal pair. Floating point NaNs are not considered equal.

The slices package import path is golang.org/x/exp/slices. Code inside exp package is experimental, not yet stable. It will be moved into the standard library <strike>in Go 1.19</strike> eventually.

Nevertheless you can use it as soon as 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, &quot;toy&quot;}, {500.49, &quot;phone&quot;}}
	sliceD := []data{{10.99, &quot;toy&quot;}, {200.0, &quot;phone&quot;}}
	equal = slices.Equal(sliceC, sliceD)
	fmt.Println(equal) // true

If the elements of the slice don't allow == and !=, you can use slices.EqualFunc and define whatever comparator function makes sense for the element type.

答案7

得分: 7

如果您有兴趣编写一个测试,那么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
英文:

In case that you are interested in writing a test, then github.com/stretchr/testify/assert is your friend.

Import the library at the very beginning of the file:

import (
    &quot;github.com/stretchr/testify/assert&quot;
)

Then inside the test you do:


func TestEquality_SomeSlice (t * testing.T) {
    a := []int{1, 2}
    b := []int{2, 1}
    assert.Equal(t, a, b)
}

The error prompted will be:

				Diff:
				--- Expected
				+++ Actual
				@@ -1,4 +1,4 @@
				 ([]int) (len=2) {
				+ (int) 1,
				  (int) 2,
				- (int) 2,
				  (int) 1,
Test:       	TestEquality_SomeSlice

答案8

得分: 2

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

foo := []int{1,3,5,7,9,11,13,15,17,19}

// 这两个切片完全相同
subslice1 := foo[3:][:4]
subslice2 := foo[:7][3:]

slicesEqual := &subslice1[0]  == &subslice2[0]   && 
               len(subslice1) == len(subslice2)

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

英文:

Thought of a neat trick and figured I'd share.

If what you are interested in knowing is whether two slices are identical (i.e. they alias the same region of data) instead of merely equal (the value at each index of one slice equals the value in the same index of the other) then you can efficiently compare them in the following way:

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 := &amp;subslice1[0]  == &amp;subslice2[0]   &amp;&amp; 
               len(subslice1) == len(subslice2)

There are some caveats to this sort of comparison, in particular that you cannot compare empty slices in this way, and that the capacity of the slices isn't compared, so this "identicality" property is only really useful when reading from a slice or reslicing a strictly narrower subslice, as any attempt to grow the slice will be affected by the slices' capacity. Still, it's very useful to be able to efficiently declare, "these two huge blocks of memory are in fact the same block, yes or no."

答案9

得分: 1

为了获得一套完整的答案:这里有一个使用泛型的解决方案。

func IsEqual[A comparable](a, b []A) bool {
    // 如果长度不同,则不相等
    if len(a) != len(b) {
        return false
    }

    // 空数组可以视为相等
    if len(a) == 0 {
        return true
    }

    // 两个指针在每次迭代中相向而行
    left := 0
    right := len(a) - 1

    for left <= right {
        if a[left] != b[left] || a[right] != b[right] {
            return false
        }

        left++
        right--
    }

    return true
}

代码使用了“两个指针”的策略,其运行时复杂度为n / 2,仍然是O(n),但比逐个线性检查少了一半的步骤。

更新:根据@doublethink13的建议修复了相等性检查的错误。

英文:

To have a complete set of answers: here is a solution with generics.

func IsEqual[A comparable](a, b []A) bool {
    // Can&#39;t be equal if length differs
	if len(a) != len(b) {
		return false
	}

	// Empty arrays trivially equal
	if len(a) == 0 {
		return true
	}

    // Two pointers going towards each other at every iteration
	left := 0
	right := len(a) - 1

	for left &lt;= right {
		if a[left] != b[left] || a[right] != b[right] {
			return false
		}

		left++
		right--
	}

	return true
}

Code uses strategy of "two pointers" which brings runtime complexity of n / 2, which is still O(n), however, twice as less steps than a linear check one-by-one.

Update: Fixed equality check bug as per @doublethink13

答案10

得分: 0

有一个函数assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])用于检查切片。

感谢Aaron的评论,这并不明显,所以我添加了强调,这个断言需要库"github.com/stretchr/testify/assert"。

英文:

There is function assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) for checking slices.

Thanks Aaron for comment, It is not obvious so I add highlight this assert requires lib "github.com/stretchr/testify/assert"

答案11

得分: 0

Golang引入了一个名为Slices的包,其中包含对任何类型的切片有用的各种函数。我们可以使用Equal函数来判断两个切片是否相等。

https://cs.opensource.google/go/x/exp/+/06a737ee:slices/slices.go;l=22

// Equal函数用于判断两个切片是否相等:长度相同且所有元素相等。如果长度不同,则返回false。
// 否则,元素按照索引递增的顺序进行比较,比较在第一对不相等的元素处停止。
// 浮点数NaN被认为不相等。
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
}

code

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)
}

英文:

Golang has introduced a package Slices with various functions useful with slices of any type. And we can use Equal function which reports whether two slices are equal.

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
}

code

package main

import (
	&quot;fmt&quot;

	&quot;golang.org/x/exp/slices&quot;
)

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

	equal := slices.Equal(s1, s2)
	fmt.Println(&quot;Is Equal ? &quot;, equal)
}

答案12

得分: -1

Go语言提供了内置的支持来实现这一点。在Golang中,reflect.DeepEqual()函数用于检查x和y是否“深度相等”。要访问此函数,需要在程序中导入reflect包。

语法:
func DeepEqual(x, y interface{}) bool

参数:此函数接受两个参数,即x和y,其值可以是任何类型。

返回值:此函数返回布尔值。

例如:
如果您想检查map_1和map_2是否相等

result := reflect.DeepEqual(map_1, map_2)

如果map_1和map_2相等,结果将为true,如果map_1和map_2不相等,结果将为false

英文:

Go language provides inbuilt support implementation for this. The reflect.DeepEqual() Function in Golang is used to check whether x and y are “deeply equal” or not. To access this function, one needs to imports the reflect package in the program.

Syntax:
func DeepEqual(x, y interface{}) bool

Parameters: This function takes two parameters with value of any type, i.e. x, y.

Return Value: This function returns the boolean value.

For example:
If you want to check whether map_1 and map_2 are equal or not

result := reflect.DeepEqual(map_1, map_2)

result will be true if map_1 and map_2 are equal, and result will false if map_1 and map_2 are not equal.

huangapple
  • 本文由 发表于 2013年3月9日 22:46:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/15311969.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定