用于存储具有重复键的键值对的数据结构

时间:2016-09-16 09:12:27

标签: dictionary go slice

我可以在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

1 个答案:

答案 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 #1example #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地图初始化键的值,然后才能实际添加"添加"价值观。通过创建自己的类型并添加方法来隐藏它。