Arrays and Slices
Learn about arrays and slices in Go, including how to declare, initialize, and manipulate them. We'll cover the differences between arrays and slices and when to use each.
Arrays and Slices in Go
Introduction
This guide explores arrays and slices, fundamental data structures in Go. We'll learn how to declare, initialize, and manipulate them. We'll also highlight the key differences between arrays and slices and when to use each.
Arrays
An array is a fixed-size sequence of elements of the same type. Arrays are declared with a specific length, and this length cannot be changed after the array is created.
Declaration and Initialization
Here's how to declare and initialize an array:
package main
import "fmt"
func main() {
// Declare an array of 5 integers, initialized to zero values.
var numbers [5]int
// Declare and initialize an array with specific values.
var fruits [3]string = [3]string{"apple", "banana", "cherry"}
// Short-hand declaration and initialization (type inference).
colors := [3]string{"red", "green", "blue"}
// Initialize array with ellipsis (...) to let Go determine the size.
countries := [...]string{"USA", "Canada", "Mexico"}
fmt.Println("Numbers:", numbers)
fmt.Println("Fruits:", fruits)
fmt.Println("Colors:", colors)
fmt.Println("Countries:", countries)
fmt.Println("Length of Countries:", len(countries)) // Get the length of the array
}
Accessing and Modifying Array Elements
You can access and modify elements using their index (starting from 0):
package main
import "fmt"
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
fmt.Println("First element:", numbers[0]) // Access the first element
numbers[2] = 35 // Modify the third element
fmt.Println("Modified array:", numbers)
}
Arrays are Values
In Go, arrays are values, not references. When you assign one array to another, a complete copy of the array is created. Changes to the copied array will not affect the original array.
package main
import "fmt"
func main() {
arr1 := [3]int{1, 2, 3}
arr2 := arr1 // Copy arr1 to arr2
arr2[0] = 100 // Modify arr2
fmt.Println("arr1:", arr1) // arr1 remains unchanged
fmt.Println("arr2:", arr2)
}
Slices
A slice is a dynamically-sized, flexible view into the elements of an array. Unlike arrays, slices do not have a fixed size. They are built on top of arrays and provide a more powerful and convenient way to work with sequences of data.
Declaration and Initialization
Here's how to declare and initialize slices:
package main
import "fmt"
func main() {
// Declare an uninitialized slice (nil slice).
var numbers []int
// Create a slice using make with a specified length.
primes := make([]int, 5) // Creates a slice of length 5, initialized to zero values
// Create a slice using make with a specified length and capacity.
scores := make([]int, 0, 10) // Length 0, capacity 10
// Slice literal initialization.
names := []string{"Alice", "Bob", "Charlie"}
fmt.Println("Numbers:", numbers)
fmt.Println("Primes:", primes)
fmt.Println("Scores:", scores)
fmt.Println("Names:", names)
fmt.Println("Length of Names:", len(names)) // Get the length of the slice
fmt.Println("Capacity of Names:", cap(names)) // Get the capacity of the slice
}
Slicing Arrays
Slices are often created by "slicing" an existing array. This creates a new slice that refers to a portion of the original array. Changes made to the underlying array through the slice will affect the slice, and vice-versa.
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
// Create a slice from index 1 (inclusive) to index 4 (exclusive).
slice1 := arr[1:4]
// Create a slice from the beginning to index 3 (exclusive).
slice2 := arr[:3]
// Create a slice from index 2 (inclusive) to the end.
slice3 := arr[2:]
// Create a slice of the entire array.
slice4 := arr[:]
fmt.Println("Array:", arr)
fmt.Println("Slice1:", slice1)
fmt.Println("Slice2:", slice2)
fmt.Println("Slice3:", slice3)
fmt.Println("Slice4:", slice4)
arr[1] = 20 // Modify the underlying array
fmt.Println("Modified Array:", arr)
fmt.Println("Slice1 after array modification:", slice1) // slice1 is affected
}
Appending to Slices
The append()
function is used to add elements to a slice. If the slice has enough capacity, the new element is added to the existing underlying array. If not, a new, larger underlying array is allocated, and the slice is updated to point to the new array.
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3}
fmt.Println("Original Slice:", numbers, "Length:", len(numbers), "Capacity:", cap(numbers))
// Append a single element.
numbers = append(numbers, 4)
fmt.Println("After appending 4:", numbers, "Length:", len(numbers), "Capacity:", cap(numbers))
// Append multiple elements.
numbers = append(numbers, 5, 6, 7)
fmt.Println("After appending 5, 6, 7:", numbers, "Length:", len(numbers), "Capacity:", cap(numbers))
// Append another slice.
moreNumbers := []int{8, 9}
numbers = append(numbers, moreNumbers...) // Use ... to unpack the slice
fmt.Println("After appending moreNumbers:", numbers, "Length:", len(numbers), "Capacity:", cap(numbers))
}
Copying Slices
The copy()
function is used to copy elements from one slice to another. It copies the minimum of the lengths of the source and destination slices.
package main
import "fmt"
func main() {
source := []int{1, 2, 3, 4, 5}
destination := make([]int, 3) // Create a destination slice with length 3
numCopied := copy(destination, source) // Copies up to len(destination) elements
fmt.Println("Source:", source)
fmt.Println("Destination:", destination)
fmt.Println("Number of elements copied:", numCopied)
}
Length and Capacity
- Length: The number of elements currently in the slice. Obtained using
len(slice)
. - Capacity: The total amount of memory allocated for the underlying array. Obtained using
cap(slice)
. A slice can be extended up to its capacity without reallocating the underlying array.
Differences Between Arrays and Slices
Feature | Array | Slice |
---|---|---|
Size | Fixed size at compile time. | Dynamically sized. |
Mutability | Size cannot be changed. | Size can be changed using append() . |
Underlying Data | Stores its own data. | Is a view into an underlying array. |
Type | The size is part of the array's type (e.g., [3]int is different from [4]int ). | The slice type doesn't include size (e.g., []int ). |
Passing to Functions | Arrays are passed by value (a copy is created). | Slices are passed by reference (the underlying array is not copied, but the slice header is). |
When to Use Arrays and Slices
- Arrays: Use arrays when you know the exact size of the data structure at compile time and you don't need to resize it. Examples include:
- Representing a fixed-size grid in a game.
- Storing a fixed number of configuration values.
- Slices: Use slices in most cases, as they provide more flexibility and convenience. Examples include:
- Reading data from a file or network stream.
- Building a list of results from a database query.
- Implementing dynamic data structures like stacks and queues.