This content originally appeared on DEV Community and was authored by Bruno Ciccarino Ξ»
Alright, let’s get into maps in Go β a super flexible data structure for storing key-value pairs. If you’ve worked with dictionaries in Python or objects in JavaScript, you’re going to feel right at home with maps. Maps give you a way to associate values with unique keys, so you can look up data quickly, check for the presence of a key, and delete entries as needed.
What is a Map Anyway?
A map is like a collection of key-value pairs where each key is unique. You use the key to look up its corresponding value, and the whole thing is built for fast lookups. Think of it as a digital address book, where each key (name) is associated with a unique value (phone number) β and you can access the value in an instant.
Declaring a Map
Creating a map in Go is pretty easy. The syntax is:
map[KeyType]ValueType
So, KeyType is the type of keys in the map (like string or int), and ValueType is the type of values (like int, string, struct, etc.). Hereβs a basic example:
ages := map[string]int{
"Alice": 25,
"Bob": 30,
}
Here, ages is a map where the keys are strings (people’s names) and the values are ints (ages).
Using make to Create a Map
When you create a map with make, Go does the behind-the-scenes setup for you. This is the preferred way if you plan to add entries later.
ages := make(map[string]int)
ages["Alice"] = 25
ages["Bob"] = 30
You can think of make as reserving memory and setting up the map so itβs ready to use β and Go will handle all the resizing magic when you add new entries.
Adding and Accessing Values
Adding values to a map is simple β just use the key inside brackets. To retrieve a value, use the key in the same way.
ages["Charlie"] = 35 // Adding a new key-value pair
fmt.Println(ages["Alice"]) // Accessing a value -> 25
If you try to access a key that doesnβt exist, Go returns the zero value for the value type. So for an int, youβd get 0; for a string, youβd get an empty string.
Checking if a Key Exists
To check if a key exists, you use the “comma ok” idiom:
age, exists := ages["Dave"]
if exists {
fmt.Println("Age of Dave:", age)
} else {
fmt.Println("Dave not found.")
}
The exists variable will be true if the key exists in the map, or false if it doesnβt. This approach is handy for avoiding unnecessary zero values when checking for a non-existent key.
Updating Values
Updating a value is as easy as re-assigning it with the same key.
ages["Alice"] = 26 // Alice just had a birthday
Deleting from a Map
To delete a key-value pair, use the delete function.
delete(ages, "Bob")
fmt.Println(ages) // Bob is no longer in the map
The delete function is safe to use on keys that donβt exist β it just wonβt do anything if the key isnβt found, so you donβt need to check first.
Looping through a Map
One of the great things about maps is that you can loop through them easily with range. Each iteration gives you both the key and the value.
for name, age := range ages {
fmt.Printf("%s is %d years old.\n", name, age)
}
This will print all the key-value pairs in the map. Keep in mind, though, that Go doesnβt guarantee any particular order for the keys β maps are unordered by design.
Map Capacity and Resizing
Unlike slices, maps donβt have a len for capacity. When you create a map with make, you can specify an initial capacity as a hint to Go. It doesnβt limit the mapβs size β itβs just a performance optimization for cases where you have a rough idea of how many elements the map will hold.
bigMap := make(map[int]string, 100) // Initial capacity of 100
This tells Go that bigMap will have about 100 entries, so it can allocate memory accordingly. But if you go beyond 100 entries, the map will just grow as needed.
Nested Maps
Yes, you can create maps of maps! This is useful for representing hierarchical data or creating multi-level lookups.
matrix := make(map[string]map[string]int)
matrix["row1"] = map[string]int{"col1": 10, "col2": 20}
matrix["row2"] = map[string]int{"col1": 30, "col2": 40}
fmt.Println(matrix["row1"]["col1"]) // 10
With nested maps, youβre only limited by your imagination β or by how complex youβre willing to make your code!
The Zero Value for a Map
Maps in Go are reference types, so the zero value for a map is nil. A nil map behaves like an empty map when reading values, but you canβt add values to it until you initialize it with make.
var names map[string]int
fmt.Println(names == nil) // true
// Uncommenting the line below will panic
// names["Alice"] = 25 // PANIC: assignment to entry in nil map
To work with a map, you need to initialize it:
names = make(map[string]int)
names["Alice"] = 25
Pitfalls and Best Practices
Use the Right Key Type: Keys should ideally be types like string, int, or other types that implement equality. Custom structs can be tricky as keys unless you handle equality carefully.
Watch Out for Unordered Loops: When you range over a map, the order of iteration is random. If you need a specific order, youβll need to collect the keys, sort them, and then access the map in that order.
Initialize Before Use: Always initialize maps with make or a literal if you plan to add entries. Accessing an uninitialized map wonβt panic, but trying to write to one will.
TL;DR on Maps
Maps are dynamic key-value stores, perfect for fast lookups and flexible data storage. Use make to create and initialize a map, and keep in mind that maps are unordered and grow automatically. Dive into maps confidently, and youβll find yourself creating all kinds of efficient and flexible data structures in no time!
This content originally appeared on DEV Community and was authored by Bruno Ciccarino Ξ»