【问题标题】:Creating a map from an array of struct pointers从结构指针数组创建映射
【发布时间】:2015-06-26 14:50:02
【问题描述】:

我有一个指向结构的指针数组。这些结构有一个name 字段。我想创建一个从名称到指向结构的指针的映射。

为什么registry 映射中的所有值都相同?

package main

import "fmt"

type Thing struct {
    Name  string
    Value int
}

type Registry map[string]*Thing

func toRegistry(things *[]Thing) Registry {

    registry := make(Registry)

    for _, thing := range *things {
        registry[thing.Name] = &thing
    }
    return registry
}

func main() {

    things := []Thing{{"thingA", 1}, {"thingB", 2}}
    registry := toRegistry(&things)
    fmt.Println(registry)

}

示例输出:map[thingB:0x10436180 thingA:0x10436180]

GO

编辑

根据@tvblah 的建议,things 已经是一片,所以没有必要指向它:

package main

import "fmt"

type Thing struct {
    Name  string
    Value int
}

type Registry map[string]*Thing

func toRegistry(things []Thing) Registry {

    registry := make(Registry)

    for _, thing := range things {
        registry[thing.Name] = &thing
    }
    return registry
}

func main() {

    things := []Thing{{"thingA", 1}, {"thingB", 2}}
    registry := toRegistry(things)
    fmt.Println(registry)

GO

【问题讨论】:

    标签: pointers dictionary struct go


    【解决方案1】:

    每个映射值都是指向单个局部变量 thing 的指针。

    一种解决方法是添加指向切片元素的指针:

    func toRegistry(things []Thing) Registry {
    
      registry := make(Registry)
    
      for i := range things {
        registry[things[i].Name] = &things[i]
      }
      return registry
    }
    

    playground

    另一种选择是将指向Thing 的指针存储在切片中:

    func toRegistry(things []*Thing) Registry {
      registry := make(Registry)
      for _, thing := range things {
        registry[thing.Name] = thing
      }
      return registry
    }
    
    func main() {
      things := []*Thing{&Thing{"thingA", 1}, &Thing{"thingB", 2}}
      registry := toRegistry(things)
      fmt.Println(registry)
    }
    

    playground

    我将函数参数从指向切片的指针更改为切片。此更改对问题上提出的问题没有影响,但通常是 Go 代码的编写方式。 Go 中很少使用指向切片的指针。

    【讨论】:

    • 这是正确的答案。该行为非常微妙,因此如果在原始示例中不清楚,&thing 将返回循环中局部变量的地址。规范说“它们的范围是“for”语句的块;它们在每次迭代中被重用。所以因为thing 是相同的变量,每次地址最终也是相同的。
    【解决方案2】:

    您可以在每次迭代时将 thing 重新分配给另一个局部变量,并将新变量存储在注册表中。

    package main
    
    import "fmt"
    
    type Thing struct {
        Name  string
        Value int
    }
    
    type Registry map[string]*Thing
    
    func toRegistry(things *[]Thing) Registry {
    
        registry := make(Registry)
    
        for _, thing := range *things {
            t := thing
            registry[thing.Name] = &t
        }
        return registry
    }
    
    func main() {
    
        things := []Thing{{"thingA", 1}, {"thingB", 2}}
        registry := toRegistry(&things)
        fmt.Println(registry)
    
    }
    

    Playground

    【讨论】:

    • @tvblah 的解决方案会更高效吗?
    • @mheiber 这三种解决方案在逻辑上是不同的。这个存储指向新分配的Thing 的指针。我的第一个解决方案存储了一个指向切片元素的指针。我的第二个解决方案存储切片中的指针。您的应用程序中的逻辑可能会决定选择。此解决方案在循环的每次迭代中分配一个Thing。我的解决方案没有。额外分配对性能的影响可以忽略不计。
    • @mheiber,由于分配了新变量,我的解决方案效率有点低。
    猜你喜欢
    • 2016-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多