Go语言的实现是否存储了map的长度?

huangapple go评论92阅读模式
英文:

Do Go implementations store the length of a map?

问题

map的长度是存储在某个地方还是每次调用len(my_map)时都会计算?

语言规范对于map的说明并不是很清楚:

map元素的数量被称为其长度。对于一个map m,可以使用内置函数len来获取其长度,并且在执行过程中可能会发生变化。可以使用赋值语句添加元素,并使用索引表达式检索元素;可以使用内置函数delete删除元素。

在“长度和容量”部分,我们看到了这样的说明:

如果s是一个string常量,表达式len(s)是常量。如果s的类型是arraypointerarray,并且表达式s不包含通道接收或(非常量)函数调用,则表达式len(s)cap(s)是常量;在这种情况下,s不会被求值。否则,lencap的调用不是常量,s会被求值。

因此,它告诉我们s不是常量并且会被求值,但它没有说明它是否像slice类型那样作为存储的值进行查找。

英文:

Does a map have its length stored somewhere or is it calculated each time we call len(my_map)?

The language spec shows this for maps, which doesn't really help:

>The number of map elements is called its length. For a map m, it can be discovered using the built-in function len and may change during execution. Elements may be added during execution using assignments and retrieved with index expressions; they may be removed with the delete built-in function.

Under the "Length and capacity" section we see this:

> The expression len(s) is constant if s is a string constant. The expressions len(s) and cap(s) are constants if the type of s is an array or pointer to an array and the expression s does not contain channel receives or (non-constant) function calls; in this case s is not evaluated. Otherwise, invocations of len and cap are not constant and s is evaluated.

So it tells us that s isn't constant and is evaluated but it doesn't state if it's looked up as a stored value like they do with the slice type.

答案1

得分: 19

我还没有检查过这些来源,但我写了一个快速的基准测试。

它测试了4个映射,其中2个键是int,2个键是string

var m1 = make(map[int]int)    // 键的类型为int,len(m1) = 10
var m2 = make(map[int]int)    // 键的类型为int,len(m2) = 1000000
var s1 = make(map[string]int) // 键的类型为string,len(s1) = 10
var s2 = make(map[string]int) // 键的类型为string,len(s2) = 1000000

"Small"映射有10个元素,"large"映射有一百万个元素。映射的填充方式如下:

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)
        }
    }
}

其他的测试函数类似,只是使用了m2s1s2。以下是测试结果:

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检查了源代码长度是被存储的。

英文:

I haven't checked the sources, but I wrote a quick benchmark.

It tests 4 maps, 2 where keys are int, 2 where keys are 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

"Small" maps have 10 elements, "large" maps have a million elements. Maps are populated like this:

func init() {
	for i := 0; i &lt; 10; i++ {
		m1[i] = i
		s1[strconv.Itoa(i)] = i
	}
	for i := 0; i &lt; 1000000; i++ {
		m2[i] = i
		s2[strconv.Itoa(i)] = i
	}
}

A benchmark function looks like this:

func BenchmarkSmallIntMap(b *testing.B) {
	for i := 0; i &lt; b.N; i++ {
		for j := 0; j &lt; 1000; j++ {
			_ = len(m1) + len(m1) + len(m1) + len(m1) + len(m1) + len(m1)
		}
	}
}

All the others are similar, but using m2, s1 and s2 obviously. Here are the results:

BenchmarkSmallIntMap     1000000              2085 ns/op
BenchmarkLargeIntMap     1000000              2087 ns/op
BenchmarkSmallStringMap  1000000              2087 ns/op
BenchmarkLargeStringMap  1000000              2086 ns/op

All are the same which pretty much tells that execution time of len(m) does not depend on the map size (number of key-value pairs), which suggests that map length is stored and not "counted" when called.

If interested, here is the complete test source: Go Playground.

And twotwotwo checked the sources, the length is stored.

huangapple
  • 本文由 发表于 2015年8月1日 12:43:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/31758325.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定