How to fix Go map randomness

Apr 18, 2020 06:29 · 351 words · 2 minute read coding golang

The problem

In Go, when you use map to store stuff and then want to iterate over the keys or value, the order of iteration is not deterministic.

The following sample code will print

m := make(map[int]int)
for i := 0; i < 8; i++ {
  m[i] = i
}

for k, v := range m {
  fmt.Println(k, v)
}

this output for one run

6 6
7 7
0 0
1 1
2 2
3 3
4 4
5 5

but for another, this total different result

7 7
0 0
1 1
2 2
3 3
4 4
5 5
6 6

Not very handy when you are designing a deterministic and reproducible environment. Fortunately, you can use an ordered map.

The solution: Ordered map

The orderedmap module from elliotchance provides an implementation of an Ordered map. It keeps instead of the native map the order of insertion

m := orderedmap.NewOrderedMap()
for i := 0; i < 10; i++ {
	m.Set(i, i)
}

for _, key := range m.Keys() {
	value, _:= m.Get(key)
	fmt.Println(key, value)
}

prints

0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9

Problem solved!

But at what cost? Well if your map is not very large, the performance impact will not be big. A benchmark is available on its README.

Here a run on my laptop

goos: linux
goarch: amd64
pkg: github.com/elliotchance/orderedmap
BenchmarkAll/BenchmarkOrderedMap_Keys-12                  212968              6351 ns/op           16385 B/op          1 allocs/op
BenchmarkAll/BenchmarkOrderedMap_Set-12                  2048703               496 ns/op             208 B/op          3 allocs/op
BenchmarkAll/BenchmarkMap_Set-12                        10569781               139 ns/op              42 B/op          0 allocs/op
BenchmarkAll/BenchmarkOrderedMap_Get-12                 34637158                31.0 ns/op             0 B/op          0 allocs/op
BenchmarkAll/BenchmarkMap_Get-12                        72673410                15.8 ns/op             0 B/op          0 allocs/op
BenchmarkAll/BenchmarkOrderedMap_Delete-12               2147473               586 ns/op             203 B/op          3 allocs/op
BenchmarkAll/BenchmarkMap_Delete-12                      6536530               208 ns/op              36 B/op          0 allocs/op
BenchmarkAll/BenchmarkOrderedMap_Iterate-12                27877             46138 ns/op           16391 B/op          1 allocs/op
BenchmarkAll/BenchmarkMap_Iterate-12                       87426             13682 ns/op               0 B/op          0 allocs/op

The iteration and set operations are 3x slower than the map. However, in my case, the overall time taken by the map very small compared to the rest of the application, so no problem using this ordered map.

Happy coding!

tweet Share