Golang中如何定义以包含指针的结构体为键的映射

Golang中如何定义以包含指针的结构体为键的映射 大家好,

我遇到了一个关于使用结构体作为映射键的问题,该结构体包含指针成员。例如:

type Person struct{
name string
height *float64
}
height1 := float64(5.3)
height2 := float64(5.3)
p1:= &Person{name:"ab", height:&height1}
p2:= &Person{name:"ab", height:&height2}
myMap := map[Person]string{p1:"hello"}
if _, found := myMap[p2]; !found{
// 上述条件成立,尽管p2和p1的数据完全相同,但指向height的指针不同
}

我想知道如何使这样的结构体能够作为映射键正常工作?在C#中,如果重写结构体/类的相等运算符,问题就能解决,但我在Go语言中没有看到类似的选项。

任何帮助都将不胜感激。

谢谢, Zia


更多关于Golang中如何定义以包含指针的结构体为键的映射的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

这是不可能的。不过,你可以创建一个字符串表示形式作为映射键,或者不使用指针。

更多关于Golang中如何定义以包含指针的结构体为键的映射的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,当结构体包含指针字段时,直接将其作为映射键会遇到问题,因为映射键的比较是基于结构体所有字段的逐值比较。对于指针字段,比较的是指针地址而不是指针指向的值,即使两个指针指向的值相同,如果地址不同,它们也被视为不同的键。

要解决这个问题,你需要实现自定义的键比较逻辑。以下是几种可行的方案:

方案1:使用值类型而不是指针类型

如果可能,将结构体中的指针字段改为值类型:

type Person struct {
    name   string
    height float64  // 直接使用值类型
}

p1 := Person{name: "ab", height: 5.3}
p2 := Person{name: "ab", height: 5.3}

myMap := map[Person]string{p1: "hello"}

if _, found := myMap[p2]; found {
    // 现在这个条件会成立,因为p1和p2的所有字段值都相同
    fmt.Println("Found p2 in map")
}

方案2:自定义键类型并实现比较方法

如果必须使用指针字段,可以定义一个包装类型并实现==操作符所需的比较方法。但Go不支持运算符重载,所以我们需要使用其他方式:

type PersonKey struct {
    name   string
    height float64
}

func (p *Person) Key() PersonKey {
    return PersonKey{
        name:   p.name,
        height: *p.height,
    }
}

// 使用辅助映射
type PersonMap struct {
    data map[PersonKey]string
}

func (pm *PersonMap) Set(p *Person, value string) {
    pm.data[p.Key()] = value
}

func (pm *PersonMap) Get(p *Person) (string, bool) {
    val, found := pm.data[p.Key()]
    return val, found
}

// 使用示例
height1 := float64(5.3)
height2 := float64(5.3)
p1 := &Person{name: "ab", height: &height1}
p2 := &Person{name: "ab", height: &height2}

pm := &PersonMap{data: make(map[PersonKey]string)}
pm.Set(p1, "hello")

if val, found := pm.Get(p2); found {
    fmt.Printf("Found p2 in map with value: %s\n", val) // 这会输出
}

方案3:使用字符串作为键

将结构体序列化为字符串作为映射键:

type Person struct {
    name   string
    height *float64
}

func (p *Person) StringKey() string {
    return fmt.Sprintf("%s:%f", p.name, *p.height)
}

// 使用示例
height1 := float64(5.3)
height2 := float64(5.3)
p1 := &Person{name: "ab", height: &height1}
p2 := &Person{name: "ab", height: &height2}

myMap := map[string]string{p1.StringKey(): "hello"}

if val, found := myMap[p2.StringKey()]; found {
    fmt.Printf("Found p2 in map with value: %s\n", val) // 这会输出
}

方案4:使用复合键

如果结构体有多个字段,可以使用字段值的组合作为映射键:

type Person struct {
    name   string
    height *float64
}

type PersonMap struct {
    data map[string]map[float64]string
}

func (pm *PersonMap) Set(p *Person, value string) {
    if pm.data[p.name] == nil {
        pm.data[p.name] = make(map[float64]string)
    }
    pm.data[p.name][*p.height] = value
}

func (pm *PersonMap) Get(p *Person) (string, bool) {
    if innerMap, ok := pm.data[p.name]; ok {
        val, found := innerMap[*p.height]
        return val, found
    }
    return "", false
}

// 使用示例
pm := &PersonMap{data: make(map[string]map[float64]string)}
pm.Set(p1, "hello")

if val, found := pm.Get(p2); found {
    fmt.Printf("Found p2 in map with value: %s\n", val)
}

推荐使用方案1(改为值类型)或方案2(自定义键类型),因为它们提供了更好的类型安全性和代码清晰度。方案3和方案4在某些特定场景下也可能有用,特别是当结构体字段较多或需要更灵活的键定义时。

回到顶部