英文:
Set of structs in Go
问题
如果我有一些要存储的结构体:
type Stuff struct {
a string
b string
}
我可以使用切片来实现,但是似乎使用一个适当的集合结构会占用更少的内存。
不幸的是,Go语言没有集合结构。大家都建议使用map[Stuff]struct{}
,但是这不起作用,因为Stuff
是一个结构体。有没有什么好的解决方案?最好不用下载库。
英文:
If I have a number of structs that I want to store:
type Stuff struct {
a string
b string
}
I can do it with a slice, but it seems like it would use less memory to use a proper set structure.
Unfortunately Go doesn't have a set structure. Everyone recommends using map[Stuff]struct{}
but that doesn't work because Stuff
is a struct. Anyone have any good solutions? Ideally without having to download a library.
答案1
得分: 5
通常,使用集合(set)和映射(map)数据结构比在普通数组或切片中存储值所需的内存更多,因为集合和映射提供了额外的功能,如唯一性或通过键检索值。
如果你想要最小的内存使用量,可以简单地将它们存储在切片中,例如[]Stuff
。如果你在多个地方使用这些值,将它们的指针存储起来可能也是有利的,例如[]*Stuff
,这样存储相同Stuff
值的地方可以存储相同的指针(而不是重复值)。
如果你只想存储唯一的结构体值,那么集合(set)确实是最方便的选择,在Go语言中可以使用map
实现。
map[Stuff]struct{}
并没有问题,它是有效的。对于映射的键类型的要求是:
对于键类型的操作数,比较运算符
==
和!=
必须完全定义;因此,键类型不能是函数、映射或切片。
Stuff
是一个结构体,在Go语言中,如果结构体的所有字段都是可比较的,那么结构体值是可比较的。如果你的Stuff
结构体是你发布的内容,它是可比较的:它只包含可比较类型string
的字段。
另外,请注意,如果你想要一个集合数据结构,如果你将bool
作为值类型(例如map[Stuff]bool
)并将true
作为值,那么会更清晰,然后你可以简单地使用索引来测试一个值是否在映射中,因为索引表达式在键(在你的情况下是Stuff
)不在映射中时会产生值类型的零值(bool
类型的false
),适当地告诉你要查找的值不在“集合”中。(如果它在映射中,与之关联的true
值是索引表达式的结果,适当地告诉它在映射中)。
英文:
Usually set and map data structures require more memory than storing a list of values in plain array or slice as set and map provide additional features efficiently like uniqueness or value retrieval by key.
If you want minimal memory usage, simply store them in a slice, e.g. []Stuff
. If you use the values in multiple places, it might also be profitable to just store their pointers, e.g. []*Stuff
and so each places that store the same Stuff
values can store the same pointer (without duplicating the value).
If you only want to store unique struct values, then indeed the set would be the most convenient choice, in Go realized with a map
.
There's nothing wrong with map[Stuff]struct{}
, it works. The requirement for the key type for maps:
> The comparison operators == and != must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice.
Stuff
is a struct, and structs in Go are comparable if:
> Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.
If your Stuff
struct is what you posted, it is comparable: it only contains fields of the comparable type string
.
Also note that if you want a set data structure, it's clearer if you use bool
as the value type (e.g. map[Stuff]bool
) and true
as the value, and then you can simply use indexing to test if a value is in the map as the index expression yields the zero value of the value type (false
for bool
) if the key (Stuff
in your case) is not in the map, properly telling the value you're looking for is not in the "set". (And if it is in the map, its associated true
value is the result of the index expression - properly telling it is in the map).
答案2
得分: 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
}
英文:
As icza said, a map of structs is a valid option.
But instead of implementing the Set yourself, I would use one of the new generic implementations that are out there since Go 1.18.
See this one for example: 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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论