为什么 Go slice 不能像数组用作键一样用作 Go 映射中的键?

Why can't Go slice be used as keys in Go maps pretty much the same way arrays can be used as keys?

为什么 Go slice(它是 Go 数组的一种实现)不能像数组用作键一样用作 Go 映射中的键?

这里是 Nigel Tao 来自 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 doing

a1 := 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 doing

s1 := 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.

要准确回答"why cant?":

Spec: Map types:

The comparison operators == and != must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice.

规范不允许未定义比较的键类型。 Spec: Comparison operators 也证实了这一点:

Slice, map, and function values are not comparable.

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

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

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

如果映射允许切片作为键正常运行,则每当修改任何切片(用作键)的切片元素时,它都必须更新其内部状态和数据结构,而这不是意料之中。

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

Array values are comparable if values of the array element type are comparable. Two array values are equal if their corresponding elements are equal.

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