go - Go slice 的相等性(恒等式)

标签 go slice

我的问题与 this question 略有不同询问如何检查 Go slice 的相等性。

像这样article建议,Go slice 是一个,由三部分组成:指向数组的指针、段的长度及其容量(段的最大长度)。然后是否可以(便宜地)检查两个这样的 slice 是否相等,因为它们指向相同的底层数组并且具有相同的长度和容量值(最好不遍历两个 slice 检查各个元素的相等性)?似乎 == 运算符未在 slice 上定义。

当我实现一个内部使用 []uint64 来表示元素的位向量 (IntSet) 时出现了这个问题,我无意中实现了一个 方法 func (*IntSet) Equals(that *IntSet) bool 可以像s.Equals(s) 那样调用。

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

func (this *IntSet) Equals(that *IntSet) bool {
    if this == that { // use equality of pointers!
        return true
    }
// omitted for brevity 
}

最佳答案

使用第一个元素的地址

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

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

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

我特意省略了容量比较,因为容量只有在 slice 被重新 slice 时才会起作用。

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

测试它:

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

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

type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
}

要获得一个reflect.SliceHeader,我们可以使用package 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 个 slice 是否相同,这意味着它们指向相同的后备数组并具有相同的长度(无论它们的容量如何):

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

关于go - Go slice 的相等性(恒等式),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53009686/

相关文章:

go - 嵌套提升的接口(interface)方法变得不可见

http - golang http.Get()获取不到内容,反而500错误。为什么?

Python,将索引列表转换为切片

go - 根据值匹配数组

Python Numpy 结构化数组(recarray)赋值到切片

arrays - 使用 for 循环创建 []map[string]interface{} 时出错 -> panic : assignment to entry in nil map

go - 我可以将golang应用程序作为后台进程运行吗? (没有nohup)

go - 重新设计循环依赖缺陷

go - 如何将 ECDSA 公钥转换为 PEM 文件

go - 高效附加到可变长度的字符串容器 (Golang)