为什么不能将Go slice(Go数组的实现)用作Go map中的键,几乎与将数组用作键一样?
答案 0 :(得分:3)
Here's Nigel Tao's answer from https://groups.google.com/forum/#!topic/golang-nuts/zYlx6sR4F8Y:
One reason is that arrays are value types. If
a0
is an[N]int
(an array) then doinga1 := a0 a1[0] = 0
will not affect
a0[0]
at all.In comparison, slices refer to an underlying array. Copying a slice value is O(1) instead of O(length). If
s0
is an[]int
(a slice) then doings1 := s0 s1[0] = 0
will affect what
s0[0]
is.http://play.golang.org/p/TVkntIsLo8
Map keys need some notion of equality. For arrays, this is simply element-wise equality. For slices, there is more than one way to define equality: one is element-wise equality, another is referring to the same array backing store. Furthermore, does map insertion need to make an (expensive) copy of the entire backing array? Copying would probably be less surprising, but it is inconsistent with what assignment does.
What should this code snippet print?
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])
Different programmers can have different expectations. To avoid confusion, we have simply decided not to allow slices as map keys for now.
答案 1 :(得分:2)
准确回答"为什么不能?":
必须为密钥类型的操作数完全定义comparison operators ==和!=;因此键类型不能功能,地图或切片。
规范不允许未定义比较的键类型。 Spec: Comparison operators也证实了这一点:
切片,地图和功能值无法比较。
有关推理,请参阅smarx的答案(引用Nigel Tao's answer)。继续阅读。
Go的地图使用hashmap实施。通常(无论编程语言如何)改变在散列映射中用作键的值可能导致未定义(或意外的最少)行为。通常,密钥的哈希码用于指定放置值(键值对)的桶。如果密钥更改并且您要求该密钥的关联值,则实现可能会查找错误的存储桶(因此报告它无法找到它),因为更改的密钥值很可能会提供不同的哈希码指定一个不同的桶。
在Go切片中,只是对基础数组的连续部分的描述符,并且分配切片值仅复制这些描述符。因此,使用切片作为键,您可以预期地图实现仅复制此切片标头(这是指向底层数组中第一个引用元素的指针,长度和容量)。它只有在哈希计算和相等才能使用这3个元素时才有效,但对我们(人类,程序员)来说,切片意味着可以通过切片头访问的元素 - 也可以修改(导致上述问题) )。
如果映射允许切片作为键,为了正常工作,只要修改了任何切片(用作键)的切片元素,就必须更新其内部状态和数据结构。预期
阵列在这方面很酷:阵列意味着它的所有元素;复制数组会复制所有元素,比较定义如下:
如果数组元素类型的值具有可比性,则数组值是可比较的。如果相应的元素相等,则两个数组值相等。
如果您修改了之前用作键的数组元素:不是问题,因为新数组(使用该修改后的元素)不等于在地图中存储和使用的原始数据,因此查询与修改后的数组相关联的值将无理由地产生任何结果,并且使用未修改的原始数组进行查询将正确地返回先前存储的值。