猿问

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

为什么 Go 切片(它是 Go 数组的一种实现)不能用作 Go 映射中的键,就像数组可以用作键一样?


哆啦的时光机
浏览 282回答 2
2回答

慕妹3242003

这是来自https://groups.google.com/forum/#!topic/golang-nuts/zYlx6sR4F8Y的 Nigel Tao 的回答:一个原因是数组是值类型。如果a0是[N]int(一个数组)那么做a1 := a0a1[0] = 0完全不会影响a0[0]。相比之下,切片指的是底层数组。复制切片值是 O(1) 而不是 O(length)。如果s0是[]int(一片)然后做s1 := s0s1[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 := s0m[s0] = trues2[0] = 9println(m[s0])println(m[s1])println(m[s2])不同的程序员可以有不同的期望。为了避免混淆,我们只是决定暂时不允许切片作为映射键。

青春有我

要回答确切的“为什么不能?”:规格:地图类型:比较运算符== 和 != 必须为键类型的操作数完全定义;因此键类型不能是函数、映射或切片。该规范不允许未定义比较的键类型。规范:比较运算符也证实了这一点:切片、映射和函数值是不可比较的。有关推理,请参阅 smarx 的回答(引用Nigel Tao 的回答)。继续阅读。Go 的 map 使用hashmap实现。通常(无论编程语言如何)对用作哈希映射中的键的值进行变异可能会导致未定义的(或最少的意外)行为。通常用key的hashcode来指定存放值(key-value对)的桶。如果键更改并且您要求该键的关联值,则实现可能会在错误的存储桶中查找(因此报告它找不到它),因为更改的键值很可能会给出不同的哈希码,这可能会指定一个不同的桶。在 Go 中,切片只是底层数组连续部分的描述符,分配切片值只会复制这些描述符。因此,使用切片作为键,您会期望映射实现只复制这个切片头(它是指向底层数组中第一个引用元素的指针,长度和容量)。它只有在哈希计算和相等性使用这三个元素时才有效,但对我们(人类,程序员)来说,切片意味着可以通过切片头访问的元素 - 也可以修改(导致上述问题) )。如果一个映射允许一个切片作为键,那么它必须在任何切片(用作键)的切片元素被修改时更新其内部状态和数据结构,这是不期望的。数组在这方面很酷:数组意味着它的所有元素;复制一个数组会复制所有元素,比较的定义如下:如果数组元素类型的值是可比较的,则数组值是可比较的。如果它们的对应元素相等,则两个数组值相等。如果您修改之前用作键的数组元素:这不是问题,因为新数组(带有修改后的元素)不等于在地图中存储和使用的原始数组,因此查询关联值使用修改后的数组将有理由不产生任何结果,并且使用未修改的原始数组进行查询将正确地返回先前存储的值。
随时随地看视频慕课网APP

相关分类

Go
我要回答