是否将map其长度存储在某处或者是否在每次调用时进行计算len(my_map)?
语言规范为地图显示了这一点,这实际上没有帮助:
map元素的数量称为其长度.对于地图m,可以使用内置函数发现它,len并且可以在执行期间进行更改.可以在执行期间使用赋值添加元素,并使用索引表达式检索元素; 可以使用delete内置函数删除它们.
在"长度和容量"部分下,我们看到:
表达
len(s)如果是常数s是一个string常数.表达式len(s)和cap(s)常量,如果类型s是aarray或pointeraarray并且表达式s不包含channel接收或(非常量)函数调用; 在这种情况下s不评估.否则,调用len和cap不是常量并s进行评估.
所以它告诉我们这s不是常数并且被评估但是它没有说明它是否像对待slice类型一样被查找为存储值.
icz*_*cza 13
我没有查看消息来源,但我写了一个快速基准.
它测试4个地图,其中2个是密钥int,2个是密钥string.
var m1 = make(map[int]int) // key is of type int, len(m1) = 10
var m2 = make(map[int]int) // key is of type int, len(m2) = 1000000
var s1 = make(map[string]int) // key is of type string, len(s1) = 10
var s2 = make(map[string]int) // key is of type string, len(s2) = 1000000
Run Code Online (Sandbox Code Playgroud)
"小"地图有10个元素,"大"地图有百万个元素.地图填充如下:
func init() {
for i := 0; i < 10; i++ {
m1[i] = i
s1[strconv.Itoa(i)] = i
}
for i := 0; i < 1000000; i++ {
m2[i] = i
s2[strconv.Itoa(i)] = i
}
}
Run Code Online (Sandbox Code Playgroud)
基准函数如下所示:
func BenchmarkSmallIntMap(b *testing.B) {
for i := 0; i < b.N; i++ {
for j := 0; j < 1000; j++ {
_ = len(m1) + len(m1) + len(m1) + len(m1) + len(m1) + len(m1)
}
}
}
Run Code Online (Sandbox Code Playgroud)
所有的人都差不多,但使用m2,s1并且s2效果显着.结果如下:
BenchmarkSmallIntMap 1000000 2085 ns/op
BenchmarkLargeIntMap 1000000 2087 ns/op
BenchmarkSmallStringMap 1000000 2087 ns/op
BenchmarkLargeStringMap 1000000 2086 ns/op
Run Code Online (Sandbox Code Playgroud)
所有都是相同的,它几乎告诉执行时间len(m)不依赖于地图大小(键值对的数量),这表明地图长度被存储而不是在被调用时"计数".
如果有兴趣,这里是完整的测试来源:Go Playground.
并且twotwotwo检查了源,长度存储.