从地图上获取一段键

在Go中是否有更简单/更好的方法从地图中获取键片?

目前我在地图上迭代并复制键到一个切片:

i := 0
keys := make([]int, len(mymap))
for k := range mymap {
keys[i] = k
i++
}
378805 次浏览

更好的方法是使用append:

keys = []int{}
for k := range mymap {
keys = append(keys, k)
}

除此之外,你就不走运了——围棋不是一种很有表现力的语言。

例如,

package main


func main() {
mymap := make(map[int]string)
keys := make([]int, 0, len(mymap))
for k := range mymap {
keys = append(keys, k)
}
}

为了提高Go的效率,最小化内存分配是很重要的。

这是一个老问题,但我有自己的看法。PeterSO的回答略显简洁,但效率略低。你已经知道它的大小,所以你甚至不需要使用append:

keys := make([]int, len(mymap))


i := 0
for k := range mymap {
keys[i] = k
i++
}

在大多数情况下,它可能不会有太大的区别,但它并没有太多的工作,并且在我的测试中(使用带有1,000,000个随机int64键的映射,然后用每个方法生成键的数组10次),直接分配数组成员比使用append快了大约20%。

尽管设置容量可以避免重新分配,但append仍然需要做额外的工作,以检查每个追加是否已达到容量。

你也可以从"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]
}

我对其他回答中描述的三种方法做了一个粗略的基准。

显然,在提取键之前预分配切片比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 (在操场上运行它会中止,因为它需要太长时间,所以,在本地运行它。)

访问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现在有泛型了。你可以用maps.Keys获取任何映射的键。

使用示例:

    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兼容性保证之外。他们的目标是在将来将它移到走1.19的std库中。

操场上:https://go.dev/play/p/fkm9PrJYTly

对于那些不喜欢导入exp包的人,你可以复制源代码:

// Keys returns the keys of the map m.
// The keys will be an indeterminate order.
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}

Vinay Pai回答的泛型版本(go 1.18+)。

// MapKeysToSlice extract keys of map as slice,
func MapKeysToSlice[K comparable, V any](m map[K]V) []K {
keys := make([]K, len(m))


i := 0
for k := range m {
keys[i] = k
i++
}
return keys
}

有一个很酷的库叫做lo

一个lodash风格的Go库,基于Go 1.18+泛型(映射,过滤器,包含,查找…)

有了这个库,你可以做很多方便的操作,如映射,过滤,减少等等。还有一些map类型的帮助程序

创建一个映射键数组。

keys := lo.Keys[string, int](map[string]int{"foo": 1, "bar": 2})
// []string{"bar", "foo"}

创建映射值的数组。

values := lo.Values[string, int](map[string]int{"foo": 1, "bar": 2})
// []int{1, 2}