Creating Maps in GoLang: Step-by-Step Tutorial | DistantJob - Remote Recruitment Agency
Tech Insights

GoLang Map Tutorial: A Step-by-Step Guide to Creating Maps in GoLang

Joana Almeida
Software Developer - - - 3 min. to read

Maps are a very powerful and versatile tool for any programmer in any language. GoLang is no exception. In Go, maps function as hash tables. Go Map time complexity provides fast O(1) data retrieval, updates, and deletions.

Whether you are building microservices or managing complex data, mastering the Golang map is key to maintaining code quality.

Here is a simple, no-nonsense breakdown of how Golang maps work. Think of a map as a digital dictionary where you look up a specific word (the Key) to find its definition (the Value).

ConceptWhat it meansHow it looks in codeWhy it matters
DeclarationTelling Go you want a map, but not creating it yet. (Starts as nil).var m map[string]intYou can’t store data in a nil map yet, or it will panic.
InitializationActually allocating memory so the map is ready to use.m = make(map[string]int)
or literal:
m := map[string]int{}
Always use make() or a map literal before adding data.
Adding / UpdatingInserting a new key-value pair or changing an existing one.m["apple"] = 5If the key “apple” already exists, its value is overwritten.
RetrievingLooking up a value using its key.score := m["apple"]If the key doesn’t exist, Go safely returns the “zero value” (like 0 or “”).
The “Comma Ok” CheckChecking if a key actually exists in the map.val, ok := m["apple"]ok is a boolean (true/false). Crucial for telling the difference between a missing key and a key whose value is just 0.
DeletingRemoving a key and its value entirely.delete(m, "apple")Safe to call even if the key isn’t in the map; it won’t crash.

Understanding GoLang Maps: An Overview

A Map in GoLang is a data structure that allows you to store data indexed by a unique key. It is an efficient way to store and retrieve data that needs a unique identifier.

This functionality shines in real-world scenarios where data needs to be quickly accessed, updated, or managed based on unique identifiers, like managing a customer records database where each customer has a unique ID. Compared to other data structures like arrays or slices, GoLang Maps offer faster lookups by key, which is a significant advantage.

Maps in GoLang use a simple yet powerful syntax to store key-value pairs:

map[KeyType]ValueType

Example Implementation:

    
    // Creating a Map in Go
    var ages map[string]int
    
    // Initializing the Map
    ages = make(map[string]int)

    // Adding Values
    ages["Alice"] = 30
    ages["Bob"] = 25

    fmt.Println(ages["Alice"]) // Output: 30
    
    

Here, KeyType is a string, and ValueType is an int, allowing you to store and retrieve integer values associated with names.

You’ll learn the steps to create and manipulate GoLang Maps, unlocking the potential to handle data efficiently in your GoLang projects.

Why Use a Map Over Arrays or Slices?

In real-world production applications, you need to manage records based on unique identifiers. Examples include looking up user profiles via a userID or checking configuration settings by a configKey.

While arrays and slices require a linear search (O(n) time complexity) to find an unindexed item, a Go map uses an underlying hash function to locate values instantly. Golang maps are the best choice for data lookups where speed is critical.

The basic syntax to declare a map in Go is straightforward:

map[KeyType]ValueType
  • KeyType: Any comparable type (e.g., string, int, bool). It cannot be a slice, map, or function because these types do not support the equality operator (==).
  • ValueType: Any data type whatsoever, including primitive types, slices, structs, or even other maps.

Data Structure Comparison: Maps vs. Slices vs. Arrays

To choose the right tool for your specific application architecture, it helps to understand how Go maps compare to sequential data structures:

FeatureGo MapGo SliceGo Array
Data OrganizationUnordered key-value pairsOrdered sequenceOrdered sequence
SizingDynamic (grows as needed)Dynamic (backed by array)Fixed-size (defined at compile time)
Key/Index TypeAny comparable type (string, int, etc.)Integer index onlyInteger index only
Lookup SpeedO(1) average via hash functionO(n) linear search (unless sorted)O(n) linear search (unless sorted)
Type CategoryReference type (points to hmap)Reference-like wrapperValue type (copied on assignment)

Go Map Time Complexity Quick Reference

An underlying hash table engine powers Go maps; therefore, Read (Lookup), Write (Insertion/Update), and Delete operations all execute with an average time complexity of O(1).

6 Simple Steps to Create a GoLang Map

Creating and manipulating maps is actually quite simple. Let’s start this Golang tutorial, where we’ll go over what you need to know to use them and how to create one.

Map Structure in GoLang

Maps are containers consisting of key-value pairs. The ‘key’ is used as an indexer for the rest of the data, or our ‘value’.

The advantage of a map is that the keys do not have to be sequential and can be inserted in any order. When any key is inserted, the map internally takes care of indexing it in a way that makes future searches fast and efficient.

1. Initializing a Map

Before using a map, you have to initialize it. There are two main ways to do this.

Method 1: Declare and Make

make() is a built-in function of Go that is used to allocate and initialize a complex data structure, such as a map. If you try to use a map before ‘making’ it, an error will occur.

This method declares the map variable, and the map has no entries.

Declare and then make
var map_name map[key_type]value_type

    // Any number of lines of code can go here, but you must not use 'map_name' in them

    map_name = make(map[key_type]value_type)

This is useful in case you need to declare the map before allocating and initializing it in some later part of your code.

Declare and make in the same line

var map_name = make(map[key_type]value_type)

Or you can use the short variable declaration operator (‘:=’).

map_name := make(map[key_type]value_type)

The map becomes immediately available to be manipulated further.

Method 2: Declare and Initialize

In this case, the map is declared and immediately filled with any number of entries you require.

 map_name := map[key_type]value_type{
        key1: value1,
        key2: value2, // remember to always use a comma, even in the last key-value pair
    }

Practical Example

We’ll be doing a practical example by developing a simple program that keeps track of the patrons of a library. We’ll be using a map to store our library’s patrons. Their Patron ID will be our ‘key’, and their name our ‘value’. The patron ID will be a simple integer, and their name a string.

Since we already want to have some patrons in our library, we’ll declare and initialize straight away.

Mr. Terrence and Ms. Evelyn work at the library and also use it, so they are our first patrons to be registered.

We’ll import the fmt package from now on so we can print our map’s contents to the screen.

Method 1

package main

import "fmt"

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

    patrons[0] = "Terrence"
    patrons[1] = "Evelyn"

    fmt.Println(patrons)
}

The output is:

map[0:Terrence 1:Evelyn]

Method 2

package main

import "fmt"

func main() {
    patrons := map[int]string{
        0: "Terrence",
        1: "Evelyn",
    }

    fmt.Println(patrons)
}

The output is:

map[0:Terrence 1:Evelyn]

2. Adding to a Map

Adding to a map is simple. All you need to do is assign a value to a key on a map. Remember that keys are unique and cannot be repeated. If the key already exists, the assignment will replace the existing value.

map_name[key] = value

Practical Example

New patrons are now registering in our library, so we’ll add them to our map and give them unique IDs.

package main

import "fmt"

func main() {
    patrons := map[int]string{
        0: "Terrence",
        1: "Evelyn",
    }

    patrons[2] = "Jonathan"
    patrons[3] = "Rick"

    fmt.Println(patrons)
}

The output is:

map[0:Terrence 1:Evelyn 2:Jonathan 3:Rick]

3. Retrieving a Value from a Golang Map

If you want to retrieve a value from a map, you do it using the key, like so:

map_name[key]

Practical Example

Let’s see who our patron 1 is.

package main

import "fmt"

func main() {
    patrons := map[int]string{
        0: "Terrence",
        1: "Evelyn",
    }

    fmt.Printf("Who is our Patron #%d? %s!", 1, patrons[1])
}

The output is:

Who is our Patron #1? Evelyn!

4. Iterating a Map

Iterating maps is usually done with a for loop combined with the range keyword.

for key, value := range map_name {
        // Process each key-value pair. You have access to both the 'key' and the 'value'.
    }

If you don’t care about a specific part of the key-value pair, you can use the blank identifier (‘_’) instead of declaring a variable.

  for _, value := range map_name {
        // Process each 'value' on the map. The key is automatically discarded.
    }
    
    for key, _ := range map_name {
        // Process each 'key' on the map. The value is automatically discarded.
    }

Practical Example

We’ve seen that you can print a whole map at once using fmt.Println(), but let’s format it a little better by ourselves, and also provide some more information about our data.

package main

import "fmt"

func main() {
    patrons := map[int]string{
        0: "Terrence",
        1: "Evelyn",
    }
	
    fmt.Printf("--- Our Patrons ---\n")
    for id, name := range patrons {
        fmt.Printf("ID: %d , Name: %s\n", id, name)
    }
		
    fmt.Printf("\n\n--- Used IDs ---\n")
    for id, _ := range patrons {
        fmt.Printf("%d, ", id)
    }
	
    fmt.Printf("\n\n--- Our Patrons' Names ---\n")
    for _, name := range patrons {
        fmt.Printf("%s, ", name)
    }    
}

The output is:

--- Our Patrons ---
ID: 0 , Name: Terrence
ID: 1 , Name: Evelyn

--- Used IDs ---
1, 0, 

--- Our Patrons' Names ---
Evelyn, Terrence, 

5. Checking if an entry exists on a Map

Assuming you’re using an assignment syntax, when we try to get a value from a map, it also returns a boolean that tells us if the key exists in the map or not. We can then use it to make checks and have code ready that is tailored to either scenario, where the key exists or doesn’t.

 returned_value, boolean_exists := map_name[key]
    if(boolean_exists){
        // You can now use 'returned_value'
    }

If the value is irrelevant to what you want to do and you just want to check that the key exists on the map, you can use the blank identifier.

_, boolean_exists := map_name[key]
    if(boolean_exists){
        // You know that the key exists on the map, but not the associated value
    }

Practical Example

Let’s check our patrons and see if an ID is being used and if so, by whom.

package main

import "fmt"

func main() {
    patrons := map[int]string{
        0: "Terrence",
        1: "Evelyn",
    }
	
    patron1_name, patron1_exists := patrons[1]
    if(patron1_exists){
        fmt.Printf("Patron #%d exists and is named %s.\n", 1, patron1_name)
    } else {
        fmt.Printf("Patron #%d does not exist.\n", 1)
    }
	
    _, patron2_exists := patrons[2]
    if(patron2_exists){
        fmt.Printf("Patron #%d exists.\n", 2)
    } else {
        fmt.Printf("Patron #%d does not exist.\n", 2)
    }
}

The output is:

Patron #1 exists and is named Evelyn.
Patron #2 does not exist.

6. Removing an entry from a Golang Map

To remove a key-value pair from a map, use the built-in function delete(). Additionally, since Go 1.21, there’s a built-in clear(m) function to remove all entries from a map.

delete(map_name, key)

Practical Example

Mr. Terrence is moving away and, unfortunately, will no longer be working at the library. As such, we need to remove him from our patrons.

package main

import "fmt"

func main() {
    patrons := map[int]string{
        0: "Terrence",
        1: "Evelyn",
    }

    fmt.Println("Before Mr. Terrence Leaves:")
    fmt.Println(patrons, "\n")

    delete(patrons, 0)

    fmt.Println("After Mr. Terrence Leaves:")
    fmt.Println(patrons, "\n")
}

The output is:

Before Mr. Terrence Leaves:
map[0:Terrence 1:Evelyn] 

After Mr. Terrence Leaves:
map[1:Evelyn] 

Remember: GoLang Maps are reference types

This is useful to remember if you’re assigning a map to another variable. Manipulating that new variable will also change the original map.

map_name := make(map[key_type]value_type)
    
    map_copy := map_name
    
    map_copy[key] = value

    // Both 'map_name' and 'map_copy' now have an entry [key:value]

Practical Example

Imagine we wanted to have separate dedicated lists for our patrons and our staff, but still include our staff as our patrons, since they also use the library.

package main

import "fmt"

func main() {
    staff := map[int]string{
        0: "Terrence",
        1: "Evelyn",
    }

    patrons := staff

    patrons [2] = "Jonathan"
    patrons [3] = "Rick"

    fmt.Println("Our Staff:")
    fmt.Println(staff, "\n")

    fmt.Println("Our Patrons:")
    fmt.Println(patrons, "\n")
}

The output is:

Our Staff:
map[0:Terrence 1:Evelyn 2:Jonathan 3:Rick] 

Our Patrons:
map[0:Terrence 1:Evelyn 2:Jonathan 3:Rick] 

This clearly would not work. We’d need to have two separate lists with repeat entries.

package main

import "fmt"

func main() {
    staff := map[int]string{
        0: "Terrence",
        1: "Evelyn",
    }
	
    patrons := make(map[int]string)

    patrons [0] = "Terrence"
    patrons [1] = "Evelyn"
    patrons [2] = "Jonathan"
    patrons [3] = "Rick"

    fmt.Println("Our Staff:")
    fmt.Println(staff, "\n")

    fmt.Println("Our Patrons:")
    fmt.Println(patrons, "\n")
}

The output is:

Our Staff:
map[0:Terrence 1:Evelyn] 

Our Patrons:
map[0:Terrence 1:Evelyn 2:Jonathan 3:Rick]

Common Mistakes When Working with GoLang Maps and How to Avoid Them

Working with maps in GoLang can be straightforward once you grasp the basics, but there are common pitfalls that developers, especially those new to GoLang, may stumble upon. Here’s a roundup of some common mistakes and how you can sidestep them:

1. Not Initializing Maps Before Use:

GoLang maps need to be initialized before they can be used.

  • Solution: Always initialize your maps using the make function or a map literal.
go
// Correct way:
myMap := make(map[string]int)
// or
myMap := map[string]int{}

2. Assuming Order in Map Iteration:

Maps in GoLang do not maintain an order of elements. Assuming an order during iteration is a mistake.

  • Solution: If order is crucial, consider using a slice to maintain order or a sorted map implementation.
go
// If order is needed, use a slice:
keys := []string{}
for key := range myMap {
    keys = append(keys, key)
}
sort.Strings(keys)  // Now keys are sorted

3. Modifying Maps During Iteration:

Adding or deleting keys during iteration can cause errors. For example, you may or may not see those changes during the same loop.

  • Solution: Collect the keys or values you want to modify in a separate slice, then iterate over that slice to make changes to the map.
go
// Collect keys:
keysToModify := []string{}
for key, value := range myMap {
    if value > 10 {
        keysToModify = append(keysToModify, key)
    }
}
// Modify map:
for _, key := range keysToModify {
    myMap[key] += 10
}

4. Ignoring the ok Check:

When accessing an element, it’s good practice to perform an ok check to see if the key exists in the map to avoid zero-value errors.

  • Solution: Always perform the ok check when accessing map elements.
go
// Correct way:
value, ok := myMap["someKey"]
if ok {
    // Key exists
} else {
    // Key does not exist
}

By being aware of these common missteps and applying the outlined solutions, you’ll be on a surer footing when working with GoLang maps.

5. Using Go Maps with Concurrent Use

Go maps are not thread-safe. If multiple goroutines attempt to read and write to the same map concurrently without synchronization, the Go runtime will trigger a fatal runtime error and crash your program.

There are two implementations of synchronization:

a. Using a sync.RWMutex (Recommended for custom structs)

Wrap your map inside a custom struct alongside a Read-Write Mutex to block conflicting concurrent operations.

go

type SafeMap struct {
    mu   sync.RWMutex
    data map[string]int
}

func (sm *SafeMap) Set(key string, val int) {
    sm.mu.Lock() // Exclusive lock for writing
    sm.data[key] = val
    sm.mu.Unlock()
}

func (sm *SafeMap) Get(key string) (int, bool) {
    sm.mu.RLock() // Shared lock for parallel reading
    val, ok := sm.data[key]
    sm.mu.RUnlock()
    return val, ok
}

b. Using sync.Map

For cases where keys are stable and read operations heavily outweigh write operations, Go provides sync.Map. However, it uses any (empty interface) types, meaning you sacrifice strict compile-time type safety.

GoLang Performance Summary Cheat Sheet

Optimizing Golang Maps requires knowledge about memory and concurrency behaviors. In the following table, we summarize everything you need to know.

OperationSyntax ExampleGo Map Time ComplexityMemory Behavior / Constraints
Initializationmake(map[k]v)O(1)Allocates the underlying hmap block
Insertionm[key] = valO(1) averagePanics if the target map is a nil map
Retrievalval, ok := m[key]O(1) averageReturns zero value if the key is missing
Deletiondelete(m, key)O(1) averageSafe to call even if the key is missing
ConcurrencyConcurrent routinesN/ALacks go map thread safety; requires a mutex

Can you create an app in Go?

Go is powerful but less suited to quick frontend-heavy app development. It’s ideal for robust, efficient backends and infrastructure-oriented applications. The standard Go libraries (such as net/http) provide strong foundations, but popular frameworks like Gin, Echo, or Fiber can help simplify development further.

Here’s an example of how to build a basic application in Golang:

Here’s the simplest way to build a basic web app in Go:

    
    package main

    import (
        "fmt"
        "net/http"
    )

    func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, Go Web App!")
    }

    func main() {
        http.HandleFunc("/", handler)
        fmt.Println("Server listening on :8080")
        http.ListenAndServe(":8080", nil)
    }
    
    

Key steps explained:

  • import packages for HTTP handling.
  • Define handler function to respond to web requests.
  • Start the web server using http.ListenAndServe.

Run your app using:

go run main.go

Then open your browser at http://localhost:8080 to see your Go app in action!

However, there are potential difficulties with Go for app development:

  • Verbose for web apps: Go is very explicit, requiring more boilerplate code than Python (Flask/Django) or Node.js (Express.js).
  • Lack of mature UI frameworks: Go isn’t ideal for building frontend or GUI-heavy applications compared to JavaScript frameworks (React, Angular, Vue).
  • Smaller ecosystem: Compared to JavaScript or Python, Go’s library ecosystem for web apps or mobile apps is less mature.

When Go excels:

  • APIs, backend microservices, or infrastructure tools
  • High concurrency & parallelism (network applications)
  • High-performance and resource-efficient apps

Conclusion

Golang Maps enable search through data. By understanding Go map time complexity constraints, initializing memory correctly to avoid nil map panics, and safeguarding your data paths using proper Go map thread safety design patterns, you can build reliable and concurrent applications in Go.

Companies deeply value Go developers for Go’s unique performance and concurrency. Moreover, corporate engineering teams heavily rely on advanced map manipulation to build low-latency API routing, optimized caching layers, and high-throughput microservices.

If your company is scaling infrastructure and looking to hire top-tier Go developers who understand these production-grade complexities, DistantJob can help you find the perfect remote talent to fit your needs.

FAQ

Are Go maps ordered?

No. Go maps have no guaranteed iteration order. Each time you range over a map, the order may differ. If you need sorted output, collect the keys into a slice, sort it, and iterate over that.

Are Go maps thread-safe?

No. Concurrent writes, or a write happening at the same time as a read, will cause a fatal runtime error. For concurrent use, either protect the map with a sync.RWMutex or use the standard library’s sync.Map.

What happens when you read a missing key from a Go map?

Go returns the zero value for the value type — 0 for int, “” for string, nil for pointers. To distinguish between a missing key and a key with a zero value, use the two-value form: value, ok := m[key].

How do you copy a Go map in Go?

You cannot copy a map by assignment (copy := original gives you a second reference to the same map). To make a true copy, iterate and assign manually, or use maps.Clone() from the maps package (available since Go 1.21).

What is the difference between a Go map and a slice?

A slice is an ordered sequence accessed by integer index. A map is an unordered collection accessed by a key of any comparable type. Use a slice when order matters or when you’re iterating sequentially. Use a map when you need fast key-based lookup.

Can you build web apps in Go?

Yes. Go is exceptionally well-suited for building robust, high-performance backends, REST APIs, and infrastructure tools. While it lacks native frontend or GUI frameworks compared to JavaScript ecosystems, Go’s standard library (specifically net/http), paired with modern, lightweight frameworks like Gin, Echo, or Fiber, makes it a powerhouse for web applications.

What types cannot be used as map keys?

Any type that cannot be compared using the standard == equality operator cannot serve as a map key. This includes slices, maps, and functions. If you attempt to declare a map using a slice key (e.g., map[[]int]string), the compiler will throw an error.

Joana Almeida

Joana Almeida (GitHub: SorceryStory) is our Technical Writer at DistantJob. With her unique background spanning software development and game design, Joana brings deep technical insights and clear communication to her writing on cutting-edge technologies, development frameworks, and collaboration tips and tools for remote dev teams.

Learn how to hire offshore people who outperform local hires

What if you could approach companies similar to yours, interview their top performers, and hire them for 50% of a North American salary?

Subscribe to our newsletter and get exclusive content and bloopers

or Share this post

Learn how to hire offshore people who outperform local hires

What if you could approach companies similar to yours, interview their top performers, and hire them for 50% of a North American salary?

Reduce Development Workload And Time With The Right Developer

When you partner with DistantJob for your next hire, you get the highest quality developers who will deliver expert work on time. We headhunt developers globally; that means you can expect candidates within two weeks or less and at a great value.

Increase your development output within the next 30 days without sacrificing quality.

Book a Discovery Call

What are your looking for?
+

Want to meet your top matching candidate?

Find professionals who connect with your mission and company.

    pop-up-img
    +

    Talk with a senior recruiter.

    Fill the empty positions in your org chart in under a month.