Skip to main content

Arrays, Slices and, Maps

In Go, arrays, slices, and maps are built-in types used for storing and working with collections of data. Each of these data structures serves a different purpose and has its own unique characteristics and use cases. Understanding how they work will help you write more efficient and effective Go code.

Arrays in Go

An array in Go is a fixed-size, ordered collection of elements of the same type. The length of an array is part of its type, meaning that arrays with different lengths are considered different types.

Key Characteristics of Arrays

  • Fixed Size: The size of an array is defined at the time of its declaration and cannot be changed.
  • Homogeneous: All elements in an array must be of the same type (e.g., all integers, all strings).
  • Memory Allocation: Arrays are stored in contiguous blocks of memory.

Declaring an Array

To declare an array in Go, you specify the type of the elements followed by the length of the array.

var arr [5]int // An array of 5 integers

You can also initialize the array at the time of declaration:

var arr = [3]int{1, 2, 3}

Alternatively, Go allows you to omit the size and let the compiler infer it:

arr := [...]int{1, 2, 3} // The compiler infers the size (3)

Accessing Elements

You can access elements of an array using an index (starting from 0):

arr[0] = 10  // Set the first element to 10
fmt.Println(arr[0]) // Get the first element

Arrays and Memory

When you pass an array to a function, Go passes the entire array by value (copying the array), so modifying the array inside the function will not affect the original array outside the function. To modify an array inside a function, you would need to pass a pointer to the array.

Example:

func modifyArray(arr [3]int) {
arr[0] = 99
}

func main() {
arr := [3]int{1, 2, 3}
modifyArray(arr)
fmt.Println(arr) // Output: [1 2 3]
}

Array Length

You can get the length of an array using the built-in len() function.

fmt.Println(len(arr)) // Output: 3

Slices in Go

A slice is a more flexible, dynamic view into an array. Unlike arrays, slices can grow and shrink in size as needed. Slices are a more common data structure in Go, and they provide powerful capabilities to work with sequences of data.

Key Characteristics of Slices

  • Dynamic Size: Unlike arrays, slices do not have a fixed size. You can append elements, and their size can grow or shrink.
  • Reference Type: Slices are reference types, meaning they point to an underlying array. When you pass a slice to a function, the function can modify the contents of the slice.
  • Literals: A slice is created using a range of elements from an existing array or using the make() function.

Declaring a Slice

Slices are typically declared in one of two ways:

  1. Using an array literal:

    slice := []int{1, 2, 3, 4} // Slice with initial elements
  2. Using make() function:

    slice := make([]int, 5) // Creates a slice of length 5 with default values (zeros)

You can also create a slice with a specific capacity:

slice := make([]int, 5, 10) // Slice with length 5 and capacity 10

Accessing and Modifying Elements

Just like arrays, you access and modify elements in a slice using indices:

slice[0] = 10  // Modify the first element
fmt.Println(slice[0]) // Get the first element

Slicing a Slice

You can create a sub-slice by specifying a range:

s := []int{1, 2, 3, 4, 5}
subSlice := s[1:4] // Slice from index 1 to 3 (not including 4)
fmt.Println(subSlice) // Output: [2 3 4]

Appending to a Slice

The append() function is used to add elements to a slice. This function returns a new slice with the added elements.

s := []int{1, 2, 3}
s = append(s, 4) // Adds 4 to the slice
fmt.Println(s) // Output: [1 2 3 4]

If the slice's underlying array is full (i.e., the slice has reached its capacity), Go automatically allocates a new, larger array to accommodate the new elements.

Passing Slices to Functions

Slices are reference types, so when passed to a function, the function can modify the underlying array directly.

func modifySlice(s []int) {
s[0] = 99
}

func main() {
slice := []int{1, 2, 3}
modifySlice(slice)
fmt.Println(slice) // Output: [99 2 3]
}

Length and Capacity

  • Length: The number of elements in the slice (len()).
  • Capacity: The number of elements the slice can accommodate before it needs to be resized (cap()).
fmt.Println(len(slice))  // Length
fmt.Println(cap(slice)) // Capacity

Maps in Go

A map is an unordered collection of key-value pairs. Maps are similar to dictionaries in Python or hash tables in other languages. The keys in a map must be of a type that is comparable (e.g., primitive types, strings, etc.).

Key Characteristics of Maps

  • Unordered: Maps do not maintain the order of elements.
  • Dynamic Size: You can add or remove key-value pairs from a map at runtime.
  • Key Lookup: You can quickly retrieve values associated with keys using a hash table-like mechanism.

Declaring a Map

Maps are declared using the make() function or map literals.

  1. Using make() function:

    m := make(map[string]int) // A map from strings to integers
  2. Using map literal:

    m := map[string]int{
    "Alice": 30,
    "Bob": 25,
    }

Adding and Accessing Elements

You can add key-value pairs to a map by using the key as the index.

m["Charlie"] = 35 // Add a key-value pair to the map
fmt.Println(m["Alice"]) // Output: 30

If you attempt to access a key that doesn't exist, it will return the zero value for the map's value type.

value := m["NonExistentKey"] // Will return the zero value (0 in this case)
fmt.Println(value) // Output: 0

Checking if a Key Exists

To check if a key exists in a map, you can use the "comma ok" idiom:

value, exists := m["Bob"]
if exists {
fmt.Println("Bob's age is", value)
} else {
fmt.Println("Bob not found")
}

Deleting Elements

You can remove key-value pairs from a map using the delete() function.

delete(m, "Bob") // Removes the key "Bob" and its associated value

Iterating Over Maps

You can use a for loop with the range keyword to iterate over the keys and values of a map.

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

Maps and Functions

Maps are reference types, so when you pass a map to a function, the function can modify the map's contents.

func modifyMap(m map[string]int) {
m["Alice"] = 40
}

func main() {
m := map[string]int{"Alice": 30, "Bob": 25}
modifyMap(m)
fmt.Println(m["Alice"]) // Output: 40
}