Go Maps

Ma Mahalakshmi V Updated 04 Nov 2026
5 min read

Go Maps

A map is Go's built-in key–value data structure. Each key is unique and points to exactly one value, which makes maps the natural choice whenever you need fast lookups by some identifier — a username, a product code, a country abbreviation — rather than by numeric position.

Compared to arrays and slices, maps are:

  • Unordered — iterating over a map gives no guaranteed order, and that order can even differ between runs of the same program.
  • Mutable — you can add, update, and delete entries freely.
  • Reference types — a map variable is a reference to an underlying hash table, not the data itself (this has real consequences, covered below).

Key Features of Maps

  • Store data as key: value pairs.
  • Keys must be unique — assigning to an existing key overwrites its value rather than creating a duplicate.
  • len(m) returns the number of key–value pairs.
  • The zero value of a map is nil, not an empty, usable map — this distinction matters and is explained below.
  • Internally implemented as a hash table, giving average constant-time lookups, inserts, and deletes.

Creating Maps

1. Map Literals

package main

import "fmt"

func main() {
    countryCodes := map[string]string{
        "IN": "India",
        "US": "United States",
        "JP": "Japan",
    }

    scores := map[string]int{
        "Alice": 90,
        "Bob":   85,
        "Eve":   88,
    }

    fmt.Println("Countries:", countryCodes)
    fmt.Println("Scores:", scores)
}

Note: because maps are unordered, running this program multiple times can print the entries in a different order each time — this is expected behavior, not a bug.

2. Using make()

package main

import "fmt"

func main() {
    inventory := make(map[string]int)

    inventory["Apples"] = 50
    inventory["Bananas"] = 30
    inventory["Oranges"] = 20

    fmt.Println("Inventory:", inventory)
}

nil Maps vs. Initialized Maps

This is one of the most important distinctions to understand about Go maps, and a frequent source of runtime panics for beginners:

var m1 = make(map[string]int) // initialized — ready to use
var m2 map[string]int         // nil map — declared, but not initialized
  • Writing to m1 works normally.
  • Writing to m2 causes a runtime panic — assignment to entry in nil map — because there is no underlying hash table for it to write into yet.
  • Reading from a nil map, by contrast, is perfectly safe: m2["anything"] simply returns the zero value for the map's value type (in this case 0), with no panic.

Rule of thumb: always initialize a map with make() or a map literal before writing to it. A var-declared map with no initializer is only safe to read from, never to write to.

Allowed Key and Value Types

Valid key types — any type that supports the == operator:

  • Numbers (int, float64, etc.)
  • Strings
  • Booleans
  • Arrays
  • Structs (as long as every field is itself comparable)
  • Pointers
  • Interfaces, if the underlying value is comparable

Invalid key types — these cannot be compared with ==, and Go refuses to compile a map using them as keys:

  • Slices
  • Maps
  • Functions

Value types can be absolutely anything, including another map or a slice.

Accessing, Adding, and Updating Elements

price := map[string]int{
    "Pen":  10,
    "Book": 50,
}

fmt.Println(price["Pen"]) // 10
cart := make(map[string]int)

cart["Milk"] = 2  // Add
cart["Bread"] = 1 // Add
cart["Milk"] = 3  // Update — overwrites the previous value for "Milk"

fmt.Println(cart)

Deleting Elements

delete(cart, "Bread")

delete() on a key that doesn't exist is safe and does nothing — it will not panic, unlike writing to a nil map.

Checking Whether a Key Exists

Indexing a map always returns two values when you ask for both: the value itself, and a bool indicating whether the key was actually present.

studentMarks := map[string]int{
    "Rahul": 80,
    "Neha":  90,
}

marks, exists := studentMarks["Rahul"]
fmt.Println(marks, exists) // 80 true

_, found := studentMarks["Amit"]
fmt.Println(found) // false

This two-value form matters because a missing key and a key whose value happens to be the zero value look identical if you only check the value: studentMarks["Amit"] alone would also return 0, indistinguishable from a student who legitimately scored 0. Checking the second return value is the only reliable way to tell "key absent" apart from "key present with a zero value."

Maps Are Reference Types

Unlike a struct or an array, a map variable doesn't hold its data directly — it holds a reference to an underlying hash table. Assigning one map variable to another does not copy the data; both variables end up pointing at the same table.

package main

import "fmt"

func main() {
    original := map[string]int{"A": 1, "B": 2}
    copyMap := original

    copyMap["A"] = 100

    fmt.Println("Original:", original) // also shows A: 100
    fmt.Println("Copy:", copyMap)
}

Expected output:

Original: map[A:100 B:2]
Copy: map[A:100 B:2]

Both variables reflect the same underlying change, because they reference the same map. This is also why passing a map to a function lets that function modify the caller's data directly — no pointer is needed, unlike with a struct.

To make an independent copy, you must explicitly create a new map and copy each entry over (Go added a built-in maps.Copy helper in the maps package starting in Go 1.21, or you can loop over the source map with range and assign each key into a fresh map yourself).

Iterating Over a Map

package main

import "fmt"

func main() {
    items := map[string]int{
        "Pen":    10,
        "Pencil": 5,
        "Eraser": 3,
    }

    for key, value := range items {
        fmt.Println(key, ":", value)
    }
}

The iteration order is not guaranteed — Go intentionally randomizes map iteration order between runs to prevent code from accidentally relying on an order that was never promised.

Iterating in a Specific Order

To guarantee a consistent order, extract the keys into a slice, sort or arrange that slice as needed, and iterate through the slice instead:

package main

import "fmt"

func main() {
    data := map[string]int{
        "one":   1,
        "two":   2,
        "three": 3,
    }

    keys := []string{"one", "two", "three"}

    for _, k := range keys {
        fmt.Println(k, ":", data[k])
    }
}

For keys whose order isn't known ahead of time, combine this with the sort package — for example, sort.Strings(keys) after collecting the keys with a loop — to iterate alphabetically. This pattern (collect keys, sort them, then range over the sorted slice) is the standard way to get deterministic output from map data in Go.

0 Comments

Reviewed before they appear

No comments yet.

Go
Ask about this post
AI Ask about this post

Ask questions about Go Maps and get answers drawn from it.

Signed-in readers only.