Maps aren't a native wire-level primitive. They're syntactic sugar for a repeated message, nothing more.
map<string, int32> items = 1;
message Entry { string key = 1; int32 value = 2; } repeated Entry items = 1;
On the wire, each entry is an ordinary length-delimited sub-message, with the key in field 1 and the value in field 2. A decoder that has never heard of maps still parses the data correctly, just as a repeated field. That's what keeps maps compatible with older Protobuf implementations.
This representation also explains a couple of things that surprise people. Entries carry no defined order. And when the same key shows up twice in a payload, the last one wins, exactly like it would when merging a repeated field.
Further Reading
- Maps in the language guide
The official maps documentation, including the backwards-compatibility note behind the repeated-entry representation on this page.
Next
Presence
Why unset and default-valued Protobuf fields take zero bytes on the wire, and how that interacts with field presence semantics when decoding.