在 golang 中修改映射值 - 有和没有指针 - 意外行为 [关闭]
Posted
技术标签:
【中文标题】在 golang 中修改映射值 - 有和没有指针 - 意外行为 [关闭]【英文标题】:Modifying a map value in golang - with and without pointers - unexpected behavior [closed] 【发布时间】:2021-02-08 02:00:39 【问题描述】:我是 golang 的新手,所以我正在尝试从其他语言(例如 Java / javascript)中调整我的想法并将其应用到 golang。在大多数情况下,这非常简单。然而,当涉及到遍历地图和修改值时,我有点难过。
考虑以下示例程序:
package main
import "fmt"
type data struct
ID string
Value string
func main()
myData := make(map[string]data)
foo := dataID: "one", Value: "oneval"
myData["one"] = foo
foo = dataID: "two", Value: "twoval"
myData["two"] = foo
for _, v := range myData
fmt.Println(v.Value)
v.Value = "kenny"
for _, v := range myData
fmt.Println(v.Value)
在大多数语言中,我希望输出是:
oneval
twoval
kenny
kenny
当然不是。它是:
oneval
twoval
oneval
twoval
感谢这个 SO 答案 (How to update map values in Go),解决方案是将元素“重新分配”回地图中。哎呀,不过还好。
另一种选择是创建一个指向结构的指针映射,但这最终导致了一个相关的悖论。考虑这个更新的程序:
package main
import "fmt"
type data struct
ID string
Value string
func main()
// Now it's a map of struct pointers...
myData := make(map[string]*data)
foo := dataID: "one", Value: "oneval"
myData["one"] = &foo
foo = dataID: "two", Value: "twoval"
myData["two"] = &foo
for _, v := range myData
fmt.Println(v.Value)
v.Value = "kenny"
for _, v := range myData
fmt.Println(v.Value)
我希望输出是:
oneval
twoval
kenny
kenny
原来是这样的:
twoval
kenny
kenny
kenny
因为当变量foo
被重新赋值时,映射中的相同值被重新赋值。什么????
因此,让它按预期工作的唯一方法是这样做:
package main
import "fmt"
type data struct
ID string
Value string
func main()
// Now it's a map of struct pointers...
myData := make(map[string]*data)
foo := dataID: "one", Value: "oneval"
myData["one"] = &foo
// Really, anything to make 'foo' go out of scope
foo := dataID: "two", Value: "twoval"
myData["two"] = &foo
for _, v := range myData
fmt.Println(v.Value)
v.Value = "kenny"
for _, v := range myData
fmt.Println(v.Value)
显然,必须有更好的方法。所以我问你们——*** 社区的聪明人——到底发生了什么?
【问题讨论】:
您应该对指针的概念有点熟悉。这确实是所有基本和预期的行为。 【参考方案1】:你声明了一个值,并存储了两个指向同一个值的指针。
以下声明 foo
并将文字 ID:"one",Value:"oneval"
分配给 is:
foo := dataID: "one", Value: "oneval"
这会在地图中存储指向foo
的指针。
myData["one"] = &foo
这会用新值ID:"two", Value:"twoval"
覆盖foo
的内容:
foo = dataID: "two", Value: "twoval"
此时,myData["one"]
指向包含ID:"two",Value:"twoval"
的foo
。
您需要存储指向两个不同内存位置的指针:
foo := dataID: "one", Value: "oneval"
myData["one"] = &foo
foo2 := dataID: "two", Value: "twoval"
myData["two"] = &foo2
或者更简单:
myData["one"] = &dataID: "one", Value: "oneval"
myData["two"] = &dataID: "two", Value: "twoval"
【讨论】:
嗯...关于覆盖 foo 的内容,这是一个有趣的观点。我认为它会创建一个全新的结构,并更新 foo 以指向新分配的结构。看来您是正确的:新结构被复制到 existing foo 结构中。感谢您的回答 - 非常感谢! 如果foo
被声明为指针,情况就会如此。
是的,我重写了代码 - 感谢你 - 使用指针,现在一切都按预期工作。再次感谢!【参考方案2】:
通过分配给变量,您不会更改其在内存中的地址。你只改变它的值。
foo := dataID: "one", Value: "oneval"
myData["one"] = &foo
foo = dataID: "two", Value: "twoval"
myData["two"] = &foo
在这里您正在更改变量foo
的值。它的地址在整个程序中保持不变。因此,当您将地址 foo
分配给 myData["one"]
并随后分配给 myData["two"]
时,实际上您为它们分配了相同的值。
fmt.Println(myData["one"] == myData["two"]) //true
【讨论】:
以上是关于在 golang 中修改映射值 - 有和没有指针 - 意外行为 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章