切成薄片的平等(身份)

时间:2018-10-26 13:19:41

标签: go slice

我的问题与this question稍有不同,后者询问如何检查Go切片的相等性。

就像article所暗示的,Go切片是一个,它由三部分组成:指向数组的指针,段的长度及其容量(最大长度为细分)。那么是否有可能(廉价地)检查两个这样的片是否相等,因为它们指向相同的基础数组并且具有相同的长度和容量值(最好不遍历两个片来检查各个元素的相等性)?似乎未在切片上定义==运算符。

问题出在我实现一个内部使用IntSet表示元素的位向量([]uint64)时,我偶然实现了 method {{1 }},其名称类似于func (*IntSet) Equals(that *IntSet) bool

(看来我可以针对这种情况进行优化,如下所示,但问题仍然存在:

s.Equals(s)

1 个答案:

答案 0 :(得分:5)

使用第一个元素的地址

最简单的方法是简单地获取切片的第一个元素的地址并进行比较(指针为comparable)。我们可以简单地使用address operator来获取第一个元素的地址,例如&s[0]。如果切片为空,则没有第一个元素,在这种情况下,我们仅检查两个元素是否为空。我们还必须比较切片的长度:

func identical(s1, s2 []int) bool {
    if len(s1) != len(s2) {
        return false
    }

    return len(s1) == 0 || &s1[0] == &s2[0]
}

我故意不比较容量,因为只有在切片后才起作用。

identical()函数仅检查切片是否相同。即使两个不相同的切片不相同,它们也可能相等(它们可能包含相等的元素)。

测试:

s := []int{1, 2, 3}
fmt.Println(identical(s, s))

s2 := []int{1, 2, 3}
fmt.Println(identical(s, s2))

输出为(在Go Playground上尝试):

true
false

使用reflect.SliceHeader

我们可能选择获取并使用包含指针,长度和容量的切片描述符。这是由reflect.SliceHeader建模的:

type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
}

要获取reflect.SliceHeader,我们可以使用包unsafeunsafe.Pointer类型,如下所示:

var s []int = ... // s is a slice

// and h will be its descriptor, of type *reflect.SliceHeader
h := (*reflect.SliceHeader)(unsafe.Pointer(&s))

一个简单的比较器函数,它检查2个条带是否相同,这意味着它们指向相同的后备阵列并且具有相同的长度(无论其容量如何):

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
}

测试:

s := []int{1, 2, 3}
fmt.Println(identical(s, s))

s2 := []int{1, 2, 3}
fmt.Println(identical(s, s2))

输出(在Go Playground上尝试):

true
false