Map 戈朗地图集
如果我有一个结构,如:Map 戈朗地图集,map,struct,go,set,Map,Struct,Go,Set,如果我有一个结构,如: type Foo struct { title string Tags map[string]string } 如何维护这样一组独特的结构?据我所知,虽然结构相等是一件事,但映射相等不是。这意味着我无法比较上述结构。因此,我不能只是实现 我能想到的两个可能有效的选项是:将标记转换为排序的[]字符串或。有人有更好的主意吗?根据您正在做的事情,一个选项可能是将结构作为值存储在映射中,而不是键。要使其工作,您需要创建某种方法,从每个结构值生成唯一的键 类似的方法可能会
type Foo struct {
title string
Tags map[string]string
}
如何维护这样一组独特的结构?据我所知,虽然结构相等是一件事,但映射相等不是。这意味着我无法比较上述结构。因此,我不能只是实现
我能想到的两个可能有效的选项是:将标记转换为排序的
[]字符串
或。有人有更好的主意吗?根据您正在做的事情,一个选项可能是将结构作为值存储在映射中,而不是键。要使其工作,您需要创建某种方法,从每个结构值生成唯一的键
类似的方法可能会奏效:
// Doesn't have to be a string: just has to be suitable for use as a map key.
func (foo *Foo) key() string {
return key_string
}
fooSet := make(map[string] *Foo)
// Store a Foo
fooSet[x.key()] = x
// Check if x is in the set:
if fooSet[x.key()] != nil {
println("x is in the set")
}
这项工作的效果取决于为结构派生键的效率。有几种方法可以实现这一点。詹姆斯·亨斯特里奇(James Henstridge)实际上有一个好主意,我试图实现它。在没有我自己的散列算法的情况下,仅仅使用map就表现得相当糟糕 我解决这个问题的方法是保留一个结构数组,然后在插入时删除任何重复的结构
package structset
type Foo struct {
title string
Tags map[string]string
}
func (f Foo) Equals(f2 Foo) bool {
if f.title != f2.title {
return false
}
if len(f.Tags) != len(f2.Tags) {
return false
}
for k, v := range f.Tags {
if w, ok := f2.Tags[k]; !ok || v != w {
return false
}
}
return true
}
type FooSet []Foo
func (this FooSet) Add(value Foo) {
if !this.Contains(value) {
this = append(this, value)
}
}
func (this FooSet) Length() int {
return len(this)
}
func (this FooSet) Contains(f Foo) bool {
for _, v := range this {
if v.Equals(f) {
return true
}
}
return false
}
func NewSet() FooSet {
return FooSet(make([]Foo, 0, 100))
}
我在我的i7-3770K Windows机器上对此进行了基准测试,并获得:
BenchmarkSmallSetWithFewCollisions 50000 46615 ns/op
BenchmarkSmallSetWithMoreCollisions 50000 46575 ns/op
BenchmarkSmallSetWithManyCollisions 50000 46605 ns/op
BenchmarkMediumSetWithFewCollisions 1000 2335296 ns/op
BenchmarkMediumSetWithMoreCollisions 1000 2352298 ns/op
BenchmarkMediumSetWithManyCollisions 1000 2336796 ns/op
BenchmarkLargeSetWithFewCollisions 50 46805944 ns/op
BenchmarkLargeSetWithMoreCollisions 50 47376016 ns/op
BenchmarkLargeSetWithManyCollisions 50 46815946 ns/op
要获得非常小的性能,可以先将所有数据插入阵列,然后删除所有重复数据
删除重复项代码为:
func (this FooSet) RemoveDuplicates() {
length := len(this) - 1
for i := 0; i < length; i++ {
for j := i + 1; j <= length; j++ {
if this[i].Equals(this[j]) {
this[j] = this[length]
this = this[0:length]
length--
j--
}
}
}
}
下面是将Foo分配给map[string]Foo的基准
BenchmarkSmallSetWithFewCollisions 50000 65718 ns/op
BenchmarkSmallSetWithMoreCollisions 50000 64238 ns/op
BenchmarkSmallSetWithManyCollisions 50000 55016 ns/op
BenchmarkMediumSetWithFewCollisions 500 3429435 ns/op
BenchmarkMediumSetWithMoreCollisions 500 3117395 ns/op
BenchmarkMediumSetWithManyCollisions 1000 2826858 ns/op
BenchmarkLargeSetWithFewCollisions 20 82635495 ns/op
BenchmarkLargeSetWithMoreCollisions 20 85285830 ns/op
BenchmarkLargeSetWithManyCollisions 20 73659350 ns/op
在我看来,即使一个映射是可散列的,它仍然不能很好地执行。您确定您的示例有效吗? 我相信您必须传递一个指向Add()方法的指针,代码才能正常工作。无论如何,以下是我的实现:
package math
// types
type IntPoint struct {
X, Y int
}
// set implementation for small number of items
type IntPointSet struct {
slice []IntPoint
}
// functions
func (p1 IntPoint) Equals(p2 IntPoint) bool {
return (p1.X == p2.X) && (p1.Y == p2.Y)
}
func (set *IntPointSet) Add(p IntPoint) {
if ! set.Contains(p) {
set.slice = append(set.slice, p)
}
}
func (set IntPointSet) Contains(p IntPoint) bool {
for _, v := range set.slice {
if v.Equals(p) {
return true
}
}
return false
}
func (set IntPointSet) NumElements() int {
return len(set.slice)
}
func NewIntPointSet() IntPointSet {
return IntPointSet{(make([]IntPoint, 0, 10))}
}
我想你可能想要
DeepEqual
。你真的需要map[string]string
,通常一个映射集是map[string]bool
?@WesFreeman:寻找结构集,而不仅仅是结构内的映射使用[]]字符串作为标记不会解决问题;切片相等性也没有定义。
package math
// types
type IntPoint struct {
X, Y int
}
// set implementation for small number of items
type IntPointSet struct {
slice []IntPoint
}
// functions
func (p1 IntPoint) Equals(p2 IntPoint) bool {
return (p1.X == p2.X) && (p1.Y == p2.Y)
}
func (set *IntPointSet) Add(p IntPoint) {
if ! set.Contains(p) {
set.slice = append(set.slice, p)
}
}
func (set IntPointSet) Contains(p IntPoint) bool {
for _, v := range set.slice {
if v.Equals(p) {
return true
}
}
return false
}
func (set IntPointSet) NumElements() int {
return len(set.slice)
}
func NewIntPointSet() IntPointSet {
return IntPointSet{(make([]IntPoint, 0, 10))}
}