我的问题与这个问题略有不同,询问如何检查Go切片的相等性.
如本文所述,Go切片是由三个部分组成的值:指向数组的指针,段的长度及其容量(段的最大长度).然后可以(便宜地)检查两个这样的切片是否相等,因为它们指向相同的底层数组并且具有相同的长度和容量值(最好不要遍历两个切片检查各个元素的相等性)?似乎==操作员未在切片上定义.
问题出现在我实现一个IntSet内部使用a []uint64来表示元素的位向量()时,我偶然发现了一个可以调用的方法 .func (*IntSet) Equals(that *IntSet) bools.Equals(s)
(看来我可以针对这种情况进行优化,如下所示,但问题仍然存在:
func (this *IntSet) Equals(that *IntSet) bool {
if this == that { // use equality of pointers!
return true
}
// omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)
最简单的方法是简单地获取切片的第一个元素的地址,并比较它们(指针是可比较的).我们可以通过简单地使用地址运算符来获取第一个元素的地址,例如&s[0].如果切片为空,则没有第一个元素,在这种情况下,我们只检查两者是否为空.我们还必须比较切片的长度:
func identical(s1, s2 []int) bool {
if len(s1) != len(s2) {
return false
}
return len(s1) == 0 || &s1[0] == &s2[0]
}
Run Code Online (Sandbox Code Playgroud)
我有目的地省略了比较容量,因为只有切片被复制才起作用.
此identical()功能仅检查切片是否相同.2个不相同的切片可以相等(它们可以包含相同的元素),即使它们不相同.
测试它:
s := []int{1, 2, 3}
fmt.Println(identical(s, s))
s2 := []int{1, 2, 3}
fmt.Println(identical(s, s2))
Run Code Online (Sandbox Code Playgroud)
输出是(在Go Playground上试试):
true
false
Run Code Online (Sandbox Code Playgroud)
reflect.SliceHeader我们可以选择获取和使用包含指针,长度和容量的切片描述符.这是建模的reflect.SliceHeader:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
Run Code Online (Sandbox Code Playgroud)
要获得a reflect.SliceHeader,我们可以使用包unsafe和unsafe.Pointer类似这样的类型:
var s []int = ... // s is a slice
// and h will be its descriptor, of type *reflect.SliceHeader
h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
Run Code Online (Sandbox Code Playgroud)
一个简单的比较器函数,用于检查两个切片是否相同,这意味着它们指向相同的后备阵列并具有相同的长度(无论其容量如何):
func identical(s1, s2 []int) bool {
h1 := (*reflect.SliceHeader)(unsafe.Pointer(&s1))
h2 := (*reflect.SliceHeader)(unsafe.Pointer(&s2))
return h1.Data == h2.Data && h1.Len == h2.Len
}
Run Code Online (Sandbox Code Playgroud)
测试它:
s := []int{1, 2, 3}
fmt.Println(identical(s, s))
s2 := []int{1, 2, 3}
fmt.Println(identical(s, s2))
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
true
false
Run Code Online (Sandbox Code Playgroud)