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 mapm
, it can be discovered using the built-in functionlen
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 ifs
is astring
constant. The expressionslen(s)
andcap(s)
are constants if the type ofs
is anarray
orpointer
to anarray
and the expressions
does not contain channel receives or (non-constant) function calls; in this cases
is not evaluated. Otherwise, invocations oflen
andcap
are not constant ands
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.
To get length of map in Go programming, call len() function and pass map as argument to it. The function returns an integer representing the number of key:value pairs in the map.
In Go language, a map is a powerful, ingenious, and versatile data structure. Golang Maps is a collection of unordered pairs of key-value. It is widely used because it provides fast lookups and values that can retrieve, update or delete with the help of keys. It is a reference to a hash table.
A map of type map[string]int with an initial capacity of 100 now requires 4176 bytes (on 64-bit arch). The default initial capacity is around 7 if not specified explicitly.
It uses a hashing function on the key to generate an index into an array of data.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With