Go语言中的结构体集合

huangapple go评论91阅读模式
英文:

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
}

huangapple
  • 本文由 发表于 2016年1月14日 18:00:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/34786552.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定