为了账号安全,请及时绑定邮箱和手机立即绑定

为什么 Go 切片不能用作 Go 映射中的键,就像数组可以用作键一样?

为什么 Go 切片不能用作 Go 映射中的键,就像数组可以用作键一样?

Go
哆啦的时光机 2022-01-17 18:42:26
为什么 Go 切片(它是 Go 数组的一种实现)不能用作 Go 映射中的键,就像数组可以用作键一样?
查看完整描述

2 回答

?
慕妹3242003

TA贡献1824条经验 获得超6个赞

这是来自https://groups.google.com/forum/#!topic/golang-nuts/zYlx6sR4F8Y的 Nigel Tao 的回答:


一个原因是数组是值类型。如果a0是[N]int(一个数组)那么做


a1 := a0

a1[0] = 0

完全不会影响a0[0]。


相比之下,切片指的是底层数组。复制切片值是 O(1) 而不是 O(length)。如果s0是[]int(一片)然后做


s1 := s0

s1[0] = 0

会影响什么s0[0]。


http://play.golang.org/p/TVkntIsLo8


映射键需要一些相等的概念。对于数组,这只是逐元素相等。对于切片,定义相等的方法不止一种:一种是元素相等,另一种是指同一个数组后备存储。此外,地图插入是否需要制作整个后备数组的(昂贵的)副本?复制可能不会那么令人惊讶,但它与赋值的作用不一致。


这个代码片段应该打印什么?


m := make(map[[]int]bool)

s0 := []int{6, 7, 8}

s1 := []int{6, 7, 8}

s2 := s0

m[s0] = true

s2[0] = 9

println(m[s0])

println(m[s1])

println(m[s2])

不同的程序员可以有不同的期望。为了避免混淆,我们只是决定暂时不允许切片作为映射键。


查看完整回答
反对 回复 2022-01-17
?
青春有我

TA贡献1784条经验 获得超8个赞

要回答确切的“为什么不能?”:

规格:地图类型:

比较运算符== 和 != 必须为键类型的操作数完全定义;因此键类型不能是函数、映射或切片

该规范不允许未定义比较的键类型。规范:比较运算符也证实了这一点:

切片、映射和函数值是不可比较的。

有关推理,请参阅 smarx 的回答(引用Nigel Tao 的回答)。继续阅读。

Go 的 map 使用hashmap实现。通常(无论编程语言如何)对用作哈希映射中的键的值进行变异可能会导致未定义的(或最少的意外)行为。通常用key的hashcode来指定存放值(key-value对)的桶。如果键更改并且您要求该键的关联值,则实现可能会在错误的存储桶中查找(因此报告它找不到它),因为更改的键值很可能会给出不同的哈希码,这可能会指定一个不同的桶。

在 Go 中,切片只是底层数组连续部分的描述符,分配切片值只会复制这些描述符。因此,使用切片作为键,您会期望映射实现只复制这个切片头(它是指向底层数组中第一个引用元素的指针,长度和容量)。它只有在哈希计算和相等性使用这三个元素时才有效,但对我们(人类,程序员)来说,切片意味着可以通过切片头访问的元素 - 也可以修改(导致上述问题) )。

如果一个映射允许一个切片作为键,那么它必须在任何切片(用作键)的切片元素被修改时更新其内部状态和数据结构,这是不期望的。

数组在这方面很酷:数组意味着它的所有元素;复制一个数组会复制所有元素,比较的定义如下:

如果数组元素类型的值是可比较的,则数组值是可比较的。如果它们的对应元素相等,则两个数组值相等。

如果您修改之前用作键的数组元素:这不是问题,因为新数组(带有修改后的元素)不等于在地图中存储和使用的原始数组,因此查询关联值使用修改后的数组将有理由不产生任何结果,并且使用未修改的原始数组进行查询将正确地返回先前存储的值。


查看完整回答
反对 回复 2022-01-17
  • 2 回答
  • 0 关注
  • 281 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信