Back to Curriculum

Arrays, Slices, and Maps

📚 Lesson 5 of 16 ⏱️ 40 min

Arrays, Slices, and Maps

40 min

Arrays in Go have fixed length determined at compile time. Once created, arrays cannot be resized. Arrays are value types—assigning an array copies all elements. Arrays are rarely used directly in Go; slices are preferred for their flexibility. Understanding arrays helps you understand slices, as slices are built on top of arrays. Arrays are useful when you know the exact size needed.

Slices are dynamic, flexible views into underlying arrays. They're one of Go's most important data structures. Slices have length (number of elements) and capacity (number of elements in underlying array). Slices can grow using `append()`, which may create a new underlying array if capacity is exceeded. Slices are reference types—assigning a slice doesn't copy the data. Understanding slices is essential for Go programming.

Maps are key-value collections, similar to dictionaries or hash tables in other languages. Maps are reference types and must be initialized with `make()` or a map literal before use. Maps provide O(1) average-case lookup, insertion, and deletion. Maps are unordered—iteration order is random. Understanding maps helps you work with key-value data efficiently.

Built-in functions for slices include `len()` (length), `cap()` (capacity), `append()` (add elements), and `copy()` (copy slice elements). The `make()` function creates slices and maps with specified length/capacity. Understanding built-in functions helps you work with these data structures effectively.

Best practices include using slices instead of arrays (more flexible), initializing maps before use, checking map key existence with the two-value assignment (`value, ok := map[key]`), using `make()` with appropriate capacity for slices when size is known, and understanding slice capacity to avoid unnecessary allocations. Understanding arrays, slices, and maps enables effective data handling in Go.

Key Concepts

  • Arrays have fixed length; slices are dynamic.
  • Slices are built on arrays and provide flexibility.
  • Maps are key-value pairs (like dictionaries).
  • Slices have length and capacity.
  • Maps must be initialized before use.

Learning Objectives

Master

  • Working with arrays and understanding their limitations
  • Creating and manipulating slices dynamically
  • Using maps for key-value data storage
  • Understanding slice capacity and growth

Develop

  • Data structure selection thinking
  • Understanding when to use slices vs arrays vs maps
  • Designing efficient data handling solutions

Tips

  • Use slices instead of arrays—they're more flexible.
  • Initialize maps with make() or map literals before use.
  • Check map key existence: value, ok := map[key].
  • Use make() with capacity when you know slice size.

Common Pitfalls

  • Trying to use arrays when slices would be better.
  • Accessing map keys without checking existence, getting zero values.
  • Not understanding slice capacity, causing unnecessary allocations.
  • Modifying slices during iteration, causing unexpected behavior.

Summary

  • Arrays have fixed length; slices are dynamic and flexible.
  • Slices are built on arrays and are preferred over arrays.
  • Maps provide efficient key-value storage.
  • Understanding these data structures is essential for Go programming.
  • Go provides built-in functions for working with slices and maps.

Exercise

Demonstrate arrays, slices, and maps operations.

package main

import "fmt"

func main() {
    // Arrays (fixed length)
    var numbers [5]int = [5]int{1, 2, 3, 4, 5}
    fmt.Printf("Array: %v, Length: %d
", numbers, len(numbers))
    
    // Slices (dynamic)
    var slice []int = []int{1, 2, 3, 4, 5}
    fmt.Printf("Slice: %v, Length: %d, Capacity: %d
", slice, len(slice), cap(slice))
    
    // Creating slices
    slice1 := make([]int, 5)    // length 5, capacity 5
    slice2 := make([]int, 3, 5) // length 3, capacity 5
    
    fmt.Printf("Slice1: %v, Length: %d, Capacity: %d
", slice1, len(slice1), cap(slice1))
    fmt.Printf("Slice2: %v, Length: %d, Capacity: %d
", slice2, len(slice2), cap(slice2))
    
    // Appending to slices
    slice = append(slice, 6, 7, 8)
    fmt.Printf("After append: %v, Length: %d, Capacity: %d
", slice, len(slice), cap(slice))
    
    // Slicing
    subSlice := slice[1:4]
    fmt.Printf("Sub-slice [1:4]: %v
", subSlice)
    
    // Maps
    person := map[string]interface{}{
        "name": "Alice",
        "age":  30,
        "city": "New York",
    }
    
    fmt.Printf("Person: %v
", person)
    
    // Accessing map values
    if name, exists := person["name"]; exists {
        fmt.Printf("Name: %v
", name)
    }
    
    // Adding to map
    person["email"] = "alice@example.com"
    fmt.Printf("Updated person: %v
", person)
    
    // Deleting from map
    delete(person, "age")
    fmt.Printf("After deleting age: %v
", person)
    
    // Iterating over slices
    fmt.Println("Iterating over slice:")
    for i, value := range slice {
        fmt.Printf("Index: %d, Value: %d
", i, value)
    }
    
    // Iterating over maps
    fmt.Println("Iterating over map:")
    for key, value := range person {
        fmt.Printf("Key: %s, Value: %v
", key, value)
    }
}

Code Editor

Output