Why is map iteration order random, and how do you iterate deterministically?
Question 57MediumGo 1.22 to 1.25
The spec says iteration order is unspecified. The runtime goes further and deliberately randomizes it: each range starts at a random position (in the classic map, a random bucket and offset). The reasons are:
- Stop people depending on it. Before Go 1.0, programs relied on insertion-like order that broke when the implementation changed.
- Allow implementation freedom. Growth, evacuation, and the Swiss-table rewrite could not keep any stable order anyway.
m := map[string]int{"b": 2, "a": 1, "c": 3}
for k := range m { fmt.Print(k) } // e.g. "bca", differs between runs
// Deterministic: sort the keys (Go 1.23 iterators)
for _, k := range slices.Sorted(maps.Keys(m)) {
fmt.Println(k, m[k])
}
// fmt prints maps sorted by key since Go 1.12
fmt.Println(m) // map[a:1 b:2 c:3]
Gotchas:
fmt.Println(m)looks ordered, which hides the problem in debug output.- Golden-file tests and hashes computed over map iteration are flaky.
encoding/jsonsorts map keys, butgobdoes not promise any order.
If you need insertion order, keep a []K next to the map, or use an ordered-map type (a map plus a linked list).
More on Arrays, Slices, Maps & Strings
- Q55Describe how Go maps were implemented before Go 1.24 (buckets, tophash, load factor, growth).
- Q56What changed in Go 1.24 with Swiss Tables? Why are they faster?
- Q58Is it safe to add or delete map entries while ranging over the map?
- Q59What happens when multiple goroutines access a map concurrently? How do you make it safe?
- Q60When should you use sync.Map instead of a map with a mutex?
- Q61Why does m["k"].Field = 1 fail to compile for map[string]T where T is a struct?