Go

Describe how Go maps were implemented before Go 1.24 (buckets, tophash, load factor, growth).

Question 55HardGo 1.22 to 1.25

The classic runtime map (runtime.hmap) is a hash table with 2^B buckets:

  • Each bucket (bmap) holds 8 entries. It starts with tophash [8]uint8, the top 8 bits of each key's hash, used as a quick filter. Then come 8 keys packed together, then 8 values packed together. Packing keys and values separately avoids padding (for example map[int64]int8). Finally there is an overflow pointer to chained buckets.
  • The low B bits of the hash pick the bucket, and the high 8 bits are compared in tophash before any full key comparison.
  • Each map gets a random hash seed, which defends against hash-flooding DoS attacks.
  • Keys or values larger than 128 bytes are stored indirectly, as pointers.

Growth happens when the average load exceeds 6.5 entries per bucket, or when there are too many overflow buckets. The first case doubles the table. The second causes a "same-size grow" that compacts chains after many deletes. Growth is incremental: oldbuckets are kept and evacuated a bucket or two at a time on each later write or delete. This spreads out the cost, so no single insert pauses for O(n).

// Pre-size to avoid repeated growth + evacuation work
m := make(map[string]int, 10_000)

Consequences interviewers probe:

  • Elements move during growth, so &m[k] is forbidden.
  • Iteration must work across old and new buckets, so the order is unspecified.
  • Maps never shrink.
  • A map value is a *hmap, so maps act like references when passed to functions.

More on Arrays, Slices, Maps & Strings

All 37 Arrays, Slices, Maps & Strings questions