在Go语言中,结构体的集合

4
如果我有一些想要存储的结构体:
type Stuff struct {
    a string
    b string
}

我可以使用切片完成,但使用适当的集合结构似乎会使用更少的内存。
不幸的是,Go没有一种集合结构。每个人都建议使用map[Stuff]struct{},但这并不起作用,因为Stuff是一个结构体。有没有什么好的解决方案?最好不用下载库。

2
地图可运行 - Ainar-G
2个回答

5
通常,设置和映射数据结构需要比将值列表存储在普通数组或切片中占用更多的内存,因为设置和映射可以有效地提供附加功能,如唯一性或通过键检索值。
如果您想要最小化内存使用,请简单地将它们存储在切片中,例如[]Stuff。如果您在多个地方使用值,则仅存储其指针可能也是有利的,例如[]*Stuff,这样存储相同Stuff值的每个位置都可以存储相同的指针(而不是重复值)。
如果您只想存储唯一的结构值,则设置确实是最方便的选择,在Go中使用map实现。 map[Stuff]struct{}没有问题,它可以正常工作。对于映射的键类型的要求

比较运算符==和!=必须完全定义为键类型的操作数;因此,键类型不能是函数、映射或切片。

Stuff是一个结构体,如果满足以下条件,Go中的结构体是可比较的:

如果所有字段都是可比较的,则结构体值是可比较的。如果它们对应的非空白字段相等,则两个结构体值相等。

如果你的Stuff结构体就是你发布的那个,那么它是可比较的:它只包含string类型的可比较字段。

也请注意,如果您想要一个集合数据结构,最好使用 bool 作为值类型(例如 map[Stuff]bool),并使用 true 作为该值。然后,您可以简单地使用 indexing 测试一个值是否在地图中作为索引表达式产生值类型的零值 (boolfalse) 如果键(Stuff在这种情况下)不在映射中,则正确地告诉您正在寻找的值不在"set"中。(如果它在地图中,它关联的 true 值是索引表达式的结果 - 正确地告诉它在地图中)。

啊,谢谢。我猜可能是因为我的真实代码中有一些字段是不可比较的。 - Timmmm
1
值得注意的是,如果你有一个巨大的集合并且内存不足,你可以使用 map[Stuff]struct{} 代替 bool 来保存一点点内存,并使用 if _, ok := m[Stuff{"a", "b"}]; ok { .... } 进行检查。 - OneOfOne

0
如icza所说,结构体映射是一个有效的选项。
但是,我建议您不要自己实现Set,而是使用自Go 1.18以来推出的新通用实现之一。
例如,可以参考这个:https://github.com/amit7itz/goset
package main

import (
    "fmt"
    "github.com/amit7itz/goset"
)

type Stuff struct {
    a string
    b string
}

func main() {
    set := goset.NewSet[Stuff]()
    set.Add(Stuff{a: "1", b: "2"})
    set.Add(Stuff{a: "2", b: "3"})
    set.Add(Stuff{a: "2", b: "3"})
    
    fmt.Println(set) // Set[main.Stuff]{{2 3} {1 2}}
    fmt.Println(set.Len()) // 2
}


最好使用本地的 map 而不是第三方库。 - Kangur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接