英文:
Do Go implementations store the length of a map?
问题
map的长度是存储在某个地方还是每次调用len(my_map)时都会计算?
语言规范对于map的说明并不是很清楚:
map元素的数量被称为其长度。对于一个mapm,可以使用内置函数len来获取其长度,并且在执行过程中可能会发生变化。可以使用赋值语句添加元素,并使用索引表达式检索元素;可以使用内置函数delete删除元素。
在“长度和容量”部分,我们看到了这样的说明:
如果
s是一个string常量,表达式len(s)是常量。如果s的类型是array或pointer到array,并且表达式s不包含通道接收或(非常量)函数调用,则表达式len(s)和cap(s)是常量;在这种情况下,s不会被求值。否则,len和cap的调用不是常量,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)
        }
    }
}
其他的测试函数类似,只是使用了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检查了源代码,长度是被存储的。
英文:
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 < 10; i++ {
		m1[i] = i
		s1[strconv.Itoa(i)] = i
	}
	for i := 0; i < 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 < b.N; i++ {
		for j := 0; j < 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论