我可以在Go中使用哪些数据结构来存储具有重复键的键值对?例如:
key | value
1 one
2 two
1 three
如果我尝试获取与密钥1
对应的值,我应该得到一个值one,three
的数组。我尝试使用map,但它只给了我一个值。
mapy := make(map[int]interface{})
mapy[1] = "one"
mapy[2] = "two"
mapy[1] = "three"
x := mapy[1]
fmt.Println(x)
output: three
答案 0 :(得分:3)
map
是一个不错的选择,但由于您希望为同一个密钥存储多个值,因此可以保证切片为值类型:
m := map[int][]interface{}{}
m[1] = append(m[1], "one")
m[2] = append(m[2], "two")
m[1] = append(m[1], "three")
fmt.Println(m[1])
输出(在Go Playground上尝试):
[one three]
请注意,使用此map
有点不太方便,因为当您想要添加新值时,您不能(不能)只是分配它,但您必须附加到与密钥关联的现有切片,您必须分配(可能)新切片。
为了简化这个" pain",您可以创建自己的类型并提供帮助方法来支持地图上的不同操作。对于后续的替代方案也是如此,因此更加冗长并不一定意味着你必须为之奋斗。
请注意,如果要在地图中存储指针,则可以避免重新分配新切片,例如:
m := map[int]*[]interface{}{}
m[1] = &[]interface{}{}
m[2] = &[]interface{}{}
s := m[1]
*s = append(*s, "one")
s = m[2]
*s = append(*s, "two")
s = m[1]
*s = append(*s, "three")
fmt.Println(m[1])
输出(在Go Playground上尝试):
&[one three]
您仍然需要将append()
返回的切片分配回来,而不是分配给地图键,而是分配给指定的值(从地图中获取/存储)。
但正如您所看到的,处理它更麻烦,但如果您经常添加新元素可能会更有效。另请注意,由于任何指针类型的零值都是nil
,因此在为键添加元素之前,首先必须使用指向现有非nil
切片的指针对其进行初始化(但是如果您创建自己的类型,则可以隐藏此检查和初始化。
以前的解决方案(使用切片中的切片)很好,但是如果你还需要支持频繁的删除操作,它们会落后,因为每当你必须删除一个元素时,你会将地图编入索引以获得切片,并且你有搜索此切片并从中删除元素(并从切片中删除包括切片头更新并将后续元素复制到1-less索引)。如果此切片未排序,则这是线性搜索,因此它具有O(n)
复杂度(其中n
是与同一个键关联的元素数,而不是映射中的键数)。根据您的情况,可能无法接受。
为了支持快速删除元素,您可以对值切片进行排序,因此在其中查找可移除元素的复杂度为O(log(n))
- 在大多数情况下都是可接受的。
更快的解决方案可能会使用另一个地图(作为一个集合,请参阅example #1和example #2)作为值类型,这也是O(1)
删除的复杂性。凉。它看起来像这样:
m := map[int]map[interface{}]bool{}
m[1] = map[interface{}]bool{}
m[2] = map[interface{}]bool{}
m[1]["one"] = true
m[2]["two"] = true
m[1]["three"] = true
fmt.Println(m[1])
输出(在Go Playground上尝试):
map[one:true three:true]
与指针切片值类型一样,您首先必须使用非nil
地图初始化键的值,然后才能实际添加"添加"价值观。通过创建自己的类型并添加方法来隐藏它。