访问映射值中的结构字段(无需复制)

huangapple go评论69阅读模式
英文:

Accessing struct fields inside a map value (without copying)

问题

假设以下代码:

type User struct {
    name string
}

users := make(map[int]User)

users[5] = User{"Steve"}

为什么无法访问存储在映射中的结构实例?

users[5].name = "Mark"

有人能解释一下如何访问存储在映射中的结构,或者为什么不可能访问吗?

注:

我知道可以通过复制结构体、更改副本并将其复制回映射来实现这一点,但这是一次昂贵的复制操作。

我也知道可以通过在映射中存储结构体指针来实现,但我也不想这样做。

英文:

Assuming the following

type User struct {
    name string
}

users := make(map[int]User)

users[5] = User{"Steve"}

Why isn't it possible to access the struct instance now stored in the map?

users[5].name = "Mark"

Can anyone shed some light into how to access the map-stored struct, or the logic behind why it's not possible?

Notes

I know that you can achieve this by making a copy of the struct, changing the copy, and copying back into the map -- but that's a costly copy operation.

I also know this can be done by storing struct pointers in my map, but I don't want to do that either.

答案1

得分: 73

基本问题是你不能在map中获取一个项的地址。你可能会认为编译器会将users[5].name = "Mark"重新排列成这样:

(&users[5]).name = "Mark"

但是这样不会编译通过,会出现以下错误:

无法获取users[5]的地址

这使得map可以自由地重新排序以有效地使用内存。

在map中显式更改某个值的唯一方法是对其赋值,即:

t := users[5]
t.name = "Mark"
users[5] = t

所以我认为你要么接受上面的复制方式,要么在map中存储指针。存储指针的缺点是使用更多的内存和更多的内存分配,这可能超过了上面的复制方式 - 只有你和你的应用程序可以判断。

第三种选择是使用切片 - 如果将users := make(map[int]User)更改为users := make([]User, 10),你的原始语法将完美地工作。

英文:

The fundamental problem is that you can't take the address of an item within a map. You might think the compiler would re-arrange users[5].name = "Mark" into this

(&users[5]).name = "Mark"

But that doesn't compile, giving this error

cannot take the address of users[5]

This gives the maps the freedom to re-order things at will to use memory efficiently.

The only way to change something explicitly in a map is to assign value to it, i.e.

t := users[5]
t.name = "Mark"
users[5] = t

So I think you either have to live with the copy above or live with storing pointers in your map. Storing pointers have the disadvantage of using more memory and more memory allocations, which may outweigh the copying way above - only you and your application can tell that.

A third alternative is to use a slice - your original syntax works perfectly if you change users := make(map[int]User) to users := make([]User, 10)

答案2

得分: 12

  1. 地图通常是稀疏填充的哈希表,当超过阈值时会重新分配。重新分配会在某人持有值的指针时产生问题。
  2. 如果你不想创建对象的副本,可以将对象本身的指针作为值存储。
  3. 当我们引用地图时,返回的值是“按值返回”的,如果我可以借用函数参数中使用的术语,编辑返回的结构对地图的内容没有任何影响。
英文:
  1. Maps are typically sparsely filled hash tables which are reallocated when they exceed the threshold. Re-allocation would create issues when someone is holding the pointers to the values
  2. If you are keen on not creating the copy of the object, you can store the pointer to the object itself as the value
  3. When we are referring the map, the value returned is returned "returned by value", if i may borrow the terminology used in function parameters, editing the returned structure does not have any impact on the contents of the map

huangapple
  • 本文由 发表于 2013年7月3日 09:12:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/17438253.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定