Do Go实现存储地图的长度?

Lye*_*ish 7 dictionary go

是否将map其长度存储在某处或者是否在每次调用时进行计算len(my_map)

语言规范为地图显示了这一点,这实际上没有帮助:

map元素的数量称为其长度.对于地图m,可以使用内置函数发现它,len并且可以在执行期间进行更改.可以在执行期间使用赋值添加元素,并使用索引表达式检索元素; 可以使用delete内置函数删除它们.

在"长度和容量"部分下,我们看到:

表达len(s)如果是常数s是一个string常数.表达式len(s)cap(s)常量,如果类型s是a arraypointera array并且表达式s不包含channel接收或(非常量)函数调用; 在这种情况下s不评估.否则,调用lencap不是常量并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检查了,长度存储.

  • 我认为这是结构def.小费计数:https://github.com/golang/go/blob/c5dff7282e27c640c192edb34b92c5c6459aa804/src/runtime/hashmap.go#L102 (5认同)