go - Go Slices 的相等性(身份)
问题描述
我的问题与询问如何检查 Go 切片的相等性的问题略有不同。
就像这篇文章所暗示的,一个 Go 切片是一个由三件事组成的值:指向数组的指针、段的长度和它的容量(段的最大长度)。那么是否可以(便宜地)检查两个这样的切片是否相等,因为它们指向同一个底层数组并且具有相同的长度和容量值(最好不遍历两个切片检查单个元素的相等性)?看来该==
运算符未在切片上定义。
当我在实现一个IntSet
内部使用 a[]uint64
来表示元素的位向量 () 时,问题出现了,我偶然发现了一个可以被称为的方法 。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
}
解决方案
使用第一个元素的地址
最简单的方法是简单地获取切片的第一个元素的地址,然后比较它们(指针是可比较的)。我们可以通过简单地使用地址运算符来获取第一个元素的地址,例如&s[0]
。如果切片为空,则没有第一个元素,在这种情况下,我们只检查两者是否为空。我们还必须比较切片的长度:
func identical(s1, s2 []int) bool {
if len(s1) != len(s2) {
return false
}
return len(s1) == 0 || &s1[0] == &s2[0]
}
我故意忽略了比较容量,因为只有在切片被重新切片时才会起作用。
此identical()
函数仅检查切片是否相同。2 个不同的切片可能相等(它们可能包含相等的元素),即使它们不相同。
测试它:
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
,我们可以使用 packageunsafe
和如下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))
一个简单的比较器函数,它检查 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
推荐阅读
- rest - 如何在 HATEOS/Hypermedia API 中正确实现链接
- amazon-web-services - 如何在 EC2 实例中提供 https 请求?
- javascript - Firebase 计划函数从 Firestore 读取数据,然后进行一些更新
- django - 使用从firebase托管到云运行的重写时,登录在django中不起作用
- javascript - 在反应中渲染哈希图数据
- sage-crm - 在 CreateScript 中强制刷新/重新加载当前页面
- python - 在 PyDbus 模拟中使用私有 DBus 连接
- bluetooth-lowenergy - 单个 BLE 模块可以连接到网格并具有点对点连接吗?
- node.js - 多个 Docker Compose:获取失败并出现错误“getaddrinfo EAI_AGAIN”
- java - 为什么找不到指定的程序?