存储具有重复键的键值对的数据结构
Data structure to store key-value pairs with duplicate keys
我可以在Go中使用哪种数据结构来存储具有重复键的键值对?例如:
key | value
1 one
2 two
1 three
并且如果我尝试获取与键 1
相对应的值,我应该得到一个值数组 one,three
。我尝试使用地图,但它只给我 1 个值。
mapy := make(map[int]interface{})
mapy[1] = "one"
mapy[2] = "two"
mapy[1] = "three"
x := mapy[1]
fmt.Println(x)
output: three
有切片值类型
如果您需要快速查找,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
切片的指针对其进行初始化(但是如果您创建自己的类型,则可以隐藏此检查和初始化)。
以map为值类型
以前的解决方案(键中有切片)很好,但是如果你还必须支持频繁的删除操作,它们就落后了,因为每当你必须删除一个元素时,你索引映射来获取切片,并且您必须搜索此切片并从中删除元素(从切片中删除包括切片 header 更新并将后续元素复制到 1-less 索引)。如果此切片未排序,则这是线性搜索,因此它具有 O(n)
复杂度(其中 n
是与同一键关联的元素数,而不是映射中的键数)。根据您的情况,可能无法接受。
为了支持快速元素移除,您可以保持值切片排序,因此在其中找到可移除元素是 O(log(n))
复杂性——在大多数情况下可以接受。
一个更快的解决方案可能使用另一个映射(作为一个集合,参见 example #1 and 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]
与 pointer-to-slice 值类型一样,您首先必须使用非 nil
映射为键初始化一个值,然后才能实际 "add" 值。通过创建您自己的类型并添加方法来隐藏它。
我可以在Go中使用哪种数据结构来存储具有重复键的键值对?例如:
key | value
1 one
2 two
1 three
并且如果我尝试获取与键 1
相对应的值,我应该得到一个值数组 one,three
。我尝试使用地图,但它只给我 1 个值。
mapy := make(map[int]interface{})
mapy[1] = "one"
mapy[2] = "two"
mapy[1] = "three"
x := mapy[1]
fmt.Println(x)
output: three
有切片值类型
如果您需要快速查找,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
切片的指针对其进行初始化(但是如果您创建自己的类型,则可以隐藏此检查和初始化)。
以map为值类型
以前的解决方案(键中有切片)很好,但是如果你还必须支持频繁的删除操作,它们就落后了,因为每当你必须删除一个元素时,你索引映射来获取切片,并且您必须搜索此切片并从中删除元素(从切片中删除包括切片 header 更新并将后续元素复制到 1-less 索引)。如果此切片未排序,则这是线性搜索,因此它具有 O(n)
复杂度(其中 n
是与同一键关联的元素数,而不是映射中的键数)。根据您的情况,可能无法接受。
为了支持快速元素移除,您可以保持值切片排序,因此在其中找到可移除元素是 O(log(n))
复杂性——在大多数情况下可以接受。
一个更快的解决方案可能使用另一个映射(作为一个集合,参见 example #1 and 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]
与 pointer-to-slice 值类型一样,您首先必须使用非 nil
映射为键初始化一个值,然后才能实际 "add" 值。通过创建您自己的类型并添加方法来隐藏它。