map
是否将其长度存储在某处或是否在每次调用len(my_map)
时进行计算?
语言规范为地图显示了这一点,这实际上没有帮助:
map
元素的数量称为其长度。对于地图m
,可以使用内置函数len
发现它,并且可以在执行期间更改。可以在执行期间使用赋值添加元素,并使用索引表达式检索元素;可以使用delete内置函数删除它们。
在“长度和容量”部分下,我们看到:
如果
len(s)
是s
常量,则表达式string
是常量。如果len(s)
的类型为cap(s)
或s
为array
且表达式为{{1},则表达式pointer
和array
是常量不包含通道接收或(非常量)函数调用;在这种情况下,不评估s
。否则,s
和len
的调用不会保持不变,并会评估cap
。
因此,它告诉我们s
不是常量并且已经过评估,但它没有说明它是否像s
类型一样被查找为存储值。
答案 0 :(得分: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
“小”地图有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
}
}
基准测试功能如下:
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)
}
}
}
其他所有内容都相似,但显然使用m2
,s1
和s2
。结果如下:
BenchmarkSmallIntMap 1000000 2085 ns/op
BenchmarkLargeIntMap 1000000 2087 ns/op
BenchmarkSmallStringMap 1000000 2087 ns/op
BenchmarkLargeStringMap 1000000 2086 ns/op
所有都是相同的,几乎可以说len(m)
的执行时间不依赖于地图大小(键值对的数量),这表明存储地图长度并且在被叫时不被“计算”。
如果有兴趣,这里是完整的测试来源:Go Playground。
twotwotwo检查sources,存储长度。