关于go:从地图中获取一些键


Getting a slice of keys from a map

在Go中,有没有更简单/更好的方法从地图中获取一个关键点?

目前我正在迭代映射并将键复制到一个切片:

1
2
3
4
5
6
i := 0
keys := make([]int, len(mymap))
for k := range mymap {
    keys[i] = k
    i++
}


这是个老问题,但这是我的两分钱。彼得索的回答稍微简洁一些,但效率稍低一些。您已经知道它将有多大,所以您甚至不需要使用append:

1
2
3
4
5
6
7
keys := make([]int, len(mymap))

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

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

尽管设置容量可以消除重新分配,但是append仍然需要做额外的工作来检查您是否已经达到了每个append的容量。


例如,

1
2
3
4
5
6
7
8
9
package main

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

为了在Go中高效运行,最小化内存分配是很重要的。


您还可以从包"reflect"中通过方法MapKeys从结构Value中获取类型为[]Value的键数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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]
}


更好的方法是使用append

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

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


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

显然,在拔出键之前预先分配切片比appending快,但令人惊讶的是,reflect.ValueOf(m).MapKeys()方法比后者慢得多:

1
2
3
4
5
6
7
8
9
10
? 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(在操场上运行它会中止声称它花费太长时间,所以,好吧,在本地运行它。)