【问题标题】:Map of structs vs array of structs in GoGo中的结构映射与结构数组
【发布时间】:2013-08-07 15:05:32
【问题描述】:

假设我有一个带有字符串属性 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

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

【问题讨论】:

  • testArray 不是“数组”。它是一个“切片”。 “数组”是不同的。

标签: map struct go slice


【解决方案1】:

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

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

【讨论】:

  • 这回答了我的问题。它不是“仅仅因为它是这样设计的”或“魔法”或其他东西,数组允许这样做而地图没有意义的原因。谢谢!
  • 另外,您的深入回答更有帮助。可能值得在这里发布所有内容,但现在,这里有一个链接:groups.google.com/d/msg/golang-nuts/FCcLsuWsF_U/qk6SLNcHvJIJ
【解决方案2】:

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

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

...然后再也不使用v。它没有效果。这相当于一开始就从不执行这两行。这就是编译器不允许你这样做的原因。另一方面,如果你把它做成一个指向 A 的 指针 的映射,你就可以做生意了。这编译:

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

这样做的原因是解引用和分配给指针值确实有效果。

【讨论】:

  • 我喜欢对地图的解释,但是数组有什么不同呢?数组也不返回指针,为什么这在数组示例中有效?
  • 我认为这只是语言的设计方式。数组很神奇。理论上他们也可以用地图做到这一点,但我想他们出于某种原因没有这样做。
  • 这是一个临时的而不是一个字面的
  • 数组并不神奇。数组或切片的索引是从其开始的偏移量。地图的索引会为您返回存储桶中项目的地址,随着更多项目的添加,该地址可能会变得无效。
  • 所以您是说m[k] = v 不是原子操作(一旦查找到值)?
【解决方案3】:

数组元素是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

解决上面的问题,可以使用指向结构体的指针:

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

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

【讨论】:

    【解决方案4】:

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

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

    ...啊。这是因为testmap["key"] 给了我们一份结构的副本。改变那个副本可能不是我们想要做的,因为它不会影响地图中的原始结构。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多