【发布时间】:2014-02-17 05:37:33
【问题描述】:
有没有更简单/更好的方法从 Go 中的映射中获取一个键片段?
目前我正在遍历地图并将键复制到切片:
i := 0
keys := make([]int, len(mymap))
for k := range mymap {
keys[i] = k
i++
}
【问题讨论】:
标签: go
有没有更简单/更好的方法从 Go 中的映射中获取一个键片段?
目前我正在遍历地图并将键复制到切片:
i := 0
keys := make([]int, len(mymap))
for k := range mymap {
keys[i] = k
i++
}
【问题讨论】:
标签: go
一个更好的方法是使用append:
keys = []int{}
for k := range mymap {
keys = append(keys, k)
}
除此之外,你很不走运——Go 不是一种表达能力很强的语言。
【讨论】:
keys = make([]int, 0, len(mymap)) 将摆脱分配,但我预计它仍然会更慢。
例如,
package main
func main() {
mymap := make(map[int]string)
keys := make([]int, 0, len(mymap))
for k := range mymap {
keys = append(keys, k)
}
}
为了在 Go 中高效,重要的是尽量减少内存分配。
【讨论】:
mymap 不是局部变量(因此会增长/缩小),这是唯一合适的解决方案 - 它确保如果 mymap 的大小在keys 和 for 循环的初始化,不会有任何越界问题。
这是一个老问题,但这是我的两分钱。 PeterSO 的回答稍微简洁一些,但效率稍低。你已经知道它会有多大,所以你甚至不需要使用 append:
keys := make([]int, len(mymap))
i := 0
for k := range mymap {
keys[i] = k
i++
}
在大多数情况下,它可能不会有太大的不同,但它并没有更多的工作,并且在我的测试中(使用带有 1,000,000 个随机 int64 键的映射,然后每个键生成十次键数组方法),直接分配数组成员比使用 append 快 20%。
虽然设置容量消除了重新分配,但追加仍然需要做额外的工作来检查每次追加是否已达到容量。
【讨论】:
for i, k := range mymap{。这样你就不需要 i++了?
i, k := range mymap,那么i 将是键,k 将是对应于映射中这些键的值。这实际上并不能帮助您填充一个键。
您还可以通过“reflect”包中的结构Value 的方法MapKeys 获取类型为[]Value 的键数组:
package main
import (
"fmt"
"reflect"
)
func main() {
abc := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
keys := reflect.ValueOf(abc).MapKeys()
fmt.Println(keys) // [a b c]
}
【讨论】:
[]string?
我对其他回复中描述的三种方法做了一个粗略的基准测试。
显然在拉键之前预分配切片比appending 快,但令人惊讶的是,reflect.ValueOf(m).MapKeys() 方法比后者慢得多:
❯ go run scratch.go
populating
filling 100000000 slots
done in 56.630774791s
running prealloc
took: 9.989049786s
running append
took: 18.948676741s
running reflect
took: 25.50070649s
这是代码:https://play.golang.org/p/Z8O6a2jyfTH (在操场上运行它会中止声称它需要太长时间,所以,好吧,在本地运行它。)
【讨论】:
keysAppend 函数中,您可以使用make([]uint64, 0, len(m)) 设置keys 数组的容量,这对我来说极大地改变了该函数的性能。
访问https://play.golang.org/p/dx6PTtuBXQW
package main
import (
"fmt"
"sort"
)
func main() {
mapEg := map[string]string{"c":"a","a":"c","b":"b"}
keys := make([]string, 0, len(mapEg))
for k := range mapEg {
keys = append(keys, k)
}
sort.Strings(keys)
fmt.Println(keys)
}
【讨论】:
最终,Go 很快就会有泛型。如果这个提案(https://github.com/golang/go/issues/47649)进入了泛型实现的第一版,我们可以通过maps.Keys获取任意map的key:
// Keys returns the keys of the map m.
// The keys will be an indeterminate order.
func Keys[M constraints.Map[K, V], K comparable, V any](m M) []K
示例使用很简单
intMap := map[int]int{1: 1, 2: 2}
intKeys := maps.Keys(intMap)
// intKeys is []int
fmt.Println(intKeys)
strMap := map[string]int{"alpha": 1, "bravo": 2}
strKeys := maps.Keys(strMap)
// strKeys is []string
fmt.Println(strKeys)
maps 包位于golang.org/x/exp/maps。这是实验性的,不属于 Go 兼容性保证。他们的目标是将其移入 Go 1.19 中的 std lib
蚂蚁方式,它适用于 gotip 游乐场 https://gotipplay.golang.org/p/fkm9PrJYTly
【讨论】: