结构的映射与Go中的结构数组

时间:2013-08-06 14:29:52

标签: map struct go slice

假设我有一个带字符串属性b的简单结构a:

type A struct {
    B string
}

以下代码使用A类型数组:

testArray := []A{A{}}
testArray[0].B = "test1"
fmt.Println(testArray[0].B)

将按预期打印出“test1”。

但这段代码似乎同样简单:

testMap := make(map[string]A)
testMap["key"] = A{}
testMap["key"].B = "test2"
fmt.Println(testMap["key"].B)

不会打印出“test2”,而是会导致以下错误:

  

无法分配到testMap [“key”]。B

那么,为什么分配给地图中的子属性导致错误,同时分配到数组中的子属性按预期工作?我想知道为什么这对地图不起作用以及为什么它适用于数组。我也很想知道他们为什么设计这两种数据结构之间存在差异的语言。

4 个答案:

答案 0 :(得分:11)

我在邮件列表上回答了一些问题,但简短的解释是这不起作用,因为地图条目不可寻址。这意味着您无法获取地图中条目的地址。这是因为向地图添加新值可能会导致地图条目四处移动,从而地址会发生变化。由于您无法获取地图中条目的地址,因此所有地图操作都使用整个值:从地图复制整个值,将整体添加到地图中。分配给地图中结构的一个字段需要进行读 - 修改 - 写操作,地图不支持(它们可以,但它们不支持,并且支持它们需要成本)。

数组和切片中的元素是可寻址的,因为它们在创建后不会移动。

答案 1 :(得分:4)

问题是在地图示例中,testMap["key"]返回文字,而不是指针。这意味着修改它是没有意义的,因此编译器不允许它。它基本上等同于:

v := testMap["key"]
v.B = "test2"

...然后再也不再使用v了。它没有效果。它相当于从不首先执行这两行。这就是编译器不允许你这样做的原因。另一方面,如果你把它作为一个指针的地图到A,你就会开展业务。这编译:

testMap := make(map[string]*A)
testMap["key"] = &A{}
testMap["key"].B = "test2"

这样做的原因是解除引用和指定指针值 会产生影响。

答案 2 :(得分:2)

数组元素是lvalue。有了地图,它会有点复杂。在:

m := map[T]U{}
m[T(expr)] = U(expr)

LHS m[T(expr)] 左值。但是,在:

type U struct{
        F V
}

m := map[T]U{}
m[T(expr)].F = 34

LHS m[T(expr)].F不再是左值。第一部分m[T(expr)]计算为U类型的实例。那个例子是“浮动的”,它再也没有家了。为其字段分配内容肯定是一个错误,因此编译器会大喊。

它与或之间的区别大致相同:

var v U
v.F = 42 // ok
U{}.F = 42 // not ok

Top解决问题,你可以使用指向结构的指针:

m := map[T]*U{}
m[T(expr)].F = 42

地图首先产生一个指向U的指针,然后用于设置字段。

答案 3 :(得分:1)

从技术上讲,根据语言参考,表达式testmap["key"].B不是addressable,因此不能用作assignment的左侧。

所以问题可能需要转移到:为什么那个表达式不可寻址?我还不太确定......

......啊。这是因为testmap["key"]正在向我们提供结构的副本。变异可能不是我们想要做的,因为它不会影响地图中的原始结构。