GO PART 6: ARRAYS AND SLICES IN GO
Please Subscribe Youtube| Like Facebook | Follow Twitter
Arrays And Slices In Go
In this article, we will provide a detailed overview of Arrays And Slices in Go and provide examples of how they are used in Go programming.
Arrays
- Arrays in Go are fixed-size collections of elements of the same type.
- The size of an array is determined at compile-time and cannot be changed.
- Array elements are accessed using zero-based indices.
- Arrays are value types, meaning when assigned to a new variable or passed as a function argument, a copy of the array is made.
Slices
- Slices are more flexible than arrays and represent dynamic arrays in Go.
- A slice is a reference to a contiguous section of an underlying array.
- Slices have a dynamic length that can be changed using built-in functions like append() and copy().
- Slice elements are accessed using zero-based indices, similar to arrays.
- When a slice is assigned to a new variable or passed as a function argument, it references the same underlying array.
Arrays
Lets first discuss Arrays in Go
Syntax of Array in Go
To declare an array in Go, you need to specify the type of elements it will hold and the number of elements it can store. The syntax for declaring an array is as follows:
var arrayName [size]dataType
Here, arrayName is the name you choose for your array, size represents the number of elements it can hold, and dataType is the type of elements the array will store. For instance, to declare an integer array of size 5, you would write:
var numbers [5]int
Initializing Arrays
Go provides several ways to initialize arrays. The most common approach is to specify the initial values within curly braces {} while declaring the array. For example:
var fruits = [3]string{"Apple", "Banana", "Orange"}
In this example, we declared and initialized an array called fruits with three elements of type string.In this example, we declared and initialized an array called fruits with three elements of type string.
Accessing Array Elements
To access individual elements in an array, you can use the index number enclosed in square brackets []. The index starts from 0 and goes up to size-1. For instance, to access the second element of the fruits array, you would write:
secondFruit := fruits[1] // "Banana"
Updating Array Elements
In addition to accessing array elements, you can also update the values of specific elements in an array. To update an element, you simply assign a new value to the desired index.
To update the first element of the fruits array, you would do the following:
fruits[0] = "Mango"
//After updating the first element to "Mango", the array will look like this:
//["Mango", "Banana", "Orange"]
Iterating Over Arrays in Go
Iterating over an array involves accessing each element of the array one by one. Go provides different approaches to iterate over arrays, such as using a traditional for loop, a range loop, or the range loop with index.
Approach 1: Using a Traditional for Loop
One way to iterate over the fruits array is by using a traditional for loop with an index variable. Here’s an example:
var fruits = [3]string{"Apple", "Banana", "Orange"}
for i := 0; i < len(fruits); i++ {
fmt.Println(fruits[i])
}
Output
Apple Banana Orange
This loop iterates from 0 to len(fruits)-1, and at each iteration, it prints the corresponding element of the fruits array.
Approach 2: Using the range Loop
The range loop is a simpler and more concise way to iterate over an array in Go. It provides both the index and value of each element. Here’s an example:
for _, fruit := range fruits {
fmt.Println(fruit)
}
Output
Apple Banana Orange
In this loop, the underscore _ is used to discard the index value since we’re not using it. The loop iterates over each element of the fruits array and assigns it to the variable fruit. Then, it prints the value of fruit in each iteration.
Approach 3: Using the range Loop with Index
If you need to access both the index and value of each element in the array, you can modify the previous example as follows:
for i, fruit := range fruits {
fmt.Printf("Index: %d, Fruit: %s\n", i, fruit)
}
Output
Index: 0, Fruit: Apple Index: 1, Fruit: Banana Index: 2, Fruit: Orange
This loop iterates over the fruits array, assigning the index to i and the value to fruit. It then prints both the index and value in each iteration. The output will be:
Sorting Arrays in Go:
Sorting arrays allows you to arrange the elements in a specific order, such as ascending or descending. Go provides a built-in package called sort that offers various sorting algorithms to sort arrays conveniently.
To demonstrate array sorting, let’s consider an example with an integer array called numbers:
var numbers = [6]int{9, 4, 7, 2, 5, 1}
Sorting in Ascending Order
To sort the numbers array in ascending order, you can use the sort.Ints() function from the sort package. Here’s an example:
import "sort"
sort.Ints(numbers[:])
fmt.Println(numbers) // Output: [1 2 4 5 7 9]
Output
[1 2 4 5 7 9]
In this code, we import the sort package and then call the Ints() function, passing numbers[:] as the argument. The [:] notation creates a slice that references the entire array. After sorting the array using Ints(), we print the sorted numbers array, which will be in ascending order.
Multidimensional Arrays in Go
A multidimensional array is an array of arrays. Go also supports multidimensional arrays, allowing you to create arrays with more than one dimension. To declare a multidimensional array, you can use the following syntax:
var matrix [3][3]int
In this example, we created a 3×3 matrix with integer elements. You can access individual elements using multiple indices, like matrix[rowIndex][columnIndex].
Initializing a 2D Array
To initialize a 2D array, you can use the following syntax:
var matrix [3][3]int = [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
In this code, we declare a 2D array called matrix with a size of 3 rows and 3 columns. Each element in the array is initialized with specific values using the nested curly braces.
Accessing Elements of a 2D Array
To access elements in the matrix array, you need to specify the row and column indices using square brackets. Here are a few examples:
element := matrix[1][2]
In this case, matrix[1] selects the second row, and [2] selects the third column within that row. The value of element will be 6.
To access an entire row or column, you can use the index without specifying the second index. For example:
row := matrix[1]
In this case, matrix[1] selects the second row of the matrix array. The row variable will be an array containing [4, 5, 6].
Traversing a Multidimensional Array in Go
To traverse and print the elements of a multidimensional array in the desired format, you can use loop as follows:
var matrix = [4][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12},
}
// Traversing and printing by row
for i := 0; i < len(matrix); i++ {
for j := 0; j < len(matrix[i]); j++ {
element := matrix[i][j]
fmt.Printf("%d ", element)
}
fmt.Println()
}
Output
1 2 3 4 5 6 7 8 9 10 11 12
In this code, the inner loop prints each element element followed by a space using fmt.Printf(“%d “, element). After printing all elements in a row, fmt.Println() is used to move to the next line, creating the desired row-wise output.
Slices
Now lets discuss slices in Go
Slice Declaration
Using an existing array
You can create a slice by specifying the range of indices from an existing array. This creates a slice that references a section of the underlying array.
The syntax is sliceName := arrayName[startIndex:endIndex]. It creates a slice that references a section of the underlying array, including elements from startIndex up to, but not including, endIndex.
array := [5]int{1, 2, 3, 4, 5}
slice := array[1:4]
In this example, slice is a slice created from the array containing elements from index 1 up to, but not including, index 4.
Using the make() function
The make() function is used to create a slice with a specified length and capacity. This allocates an underlying array and creates a slice based on it.
slice := make([]int, 3, 5)
This creates a slice named slice with a length of 3 and a capacity of 5.
Short declaration
You can declare and initialize a slice directly using the short declaration syntax.
slice := []int{1, 2, 3}
This creates a slice named slice with elements 1, 2, and 3.
Accessing Slice Elements
You can access individual elements of a slice using the index notation.
fmt.Println(slice[0]) // Output: 1
Modifying Slice
Appending elements
To add elements to a slice, you can use the append() function.
slice = append(slice, 4, 5)
This appends elements 4 and 5 to the end of the slice.
Slicing
You can create a new slice from an existing slice using slicing.
newSlice := slice[1:3]
This creates a new slice named newSlice that contains elements from index 1 up to, but not including, index 3 of the original slice.
Modifying a specific element
You can modify a specific element of a slice by assigning a new value to the corresponding index.
slice[0] = 10
This changes the value of the first element of the slice to 10.
Length and Capacity
Length: The length of a slice can be obtained using the len() function.
fmt.Println(len(slice)) // Output: 5
This prints the number of elements in the slice.
Capacity: The capacity of a slice represents the maximum number of elements it can hold without allocating a new underlying array. The capacity can be obtained using the cap() function.
fmt.Println(cap(slice)) // Output: 8
This prints the capacity of the slice.
Iterate over a slice in Go
Here’s an example of how to iterate over a slice in Go
package main
import "fmt"
func main() {
slice := []string{"Apple", "Banana", "Orange"}
// Method 1: Using a for loop with range
for index, value := range slice {
fmt.Printf("Index: %d, Value: %s\n", index, value)
}
// Method 2: Using a for loop with indexing
for i := 0; i < len(slice); i++ {
fmt.Printf("Index: %d, Value: %s\n", i, slice[i])
}
}
Output
Index: 0, Value: Apple Index: 1, Value: Banana Index: 2, Value: Orange Index: 0, Value: Apple Index: 1, Value: Banana Index: 2, Value: Orange
In this example, we have a slice slice containing three elements: “Apple”, “Banana”, and “Orange”. We iterate over the slice using two different methods:
Method 1: Using a for loop with range. The range keyword returns both the index and value of each element in the slice. We use the index and value variables to access and print the index and value of each element.
Method 2: Using a for loop with indexing. We initialize a loop counter i to 0 and iterate until i reaches the length of the slice. Inside the loop, we access and print the index and value of each element using the loop counter i.
Both methods will produce the same output, which displays the index and value of each element in the slice.
Differences between arrays and slices in Go
Here’s a table highlighting the key differences between arrays and slices in Go
Arrays | Slices |
---|---|
Fixed size, determined at compile-time | Dynamic size, can be modified |
Elements are stored sequentially in contiguous memory | Elements are stored as references to an underlying array |
Length is fixed and cannot be changed | Length can be modified by appending or removing elements |
Declared using square brackets with a specific size | Declared using square brackets with no size or using make() function |
Values are copied when assigned or passed to a function | Slice header is copied, underlying array is shared |
Limited functionality and built-in operations | Additional functions like append() and copy() available |
Better performance and memory efficiency for fixed-size collections | Flexibility to modify length and add/remove elements |
Best practices for working with arrays and slices in Go:
- Use slices when flexibility is needed: Slices provide dynamic sizing and convenient built-in functions, such as append() and copy(), making them more versatile than arrays. Use slices when you need to modify the length or add/remove elements.
- Prefer slices over arrays: Slices are commonly used in Go due to their flexibility. Unless you have a specific requirement for a fixed-size collection, it’s usually more convenient to work with slices.
- Initialize slices with make(): When creating a slice with a specific length and capacity, use the make() function. It ensures that the underlying array is properly allocated, and the length and capacity are set correctly.
- Use the range keyword for iteration: The range keyword provides a concise and efficient way to iterate over arrays and slices. It returns both the index and value of each element, simplifying the iteration process.
- Be mindful of underlying array modifications: Remember that slices are references to underlying arrays. Modifying the underlying array affects all slices referencing it. If you need independent copies, use the copy() function to create a new slice with a separate underlying array.
- Avoid unnecessary copying: When passing slices to functions or assigning them to new variables, keep in mind that a copy of the slice header is made. It doesn’t duplicate the underlying array. Only make copies when necessary to optimize memory usage.
- Use array when size is fixed and known: If you have a fixed-size collection with a known size, use an array. Arrays have a fixed size determined at compile-time, which can provide better performance and memory efficiency.
Example code with output:
package main
import "fmt"
func main() {
// Use slices when flexibility is needed
slice := []int{1, 2, 3}
slice = append(slice, 4) // Adding element to the slice
fmt.Println("Slice:", slice)
// Prefer slices over arrays
preferredSlice := []string{"Apple", "Banana", "Orange"}
fmt.Println("Preferred Slice:", preferredSlice)
// Initialize slices with make()
makeSlice := make([]int, 5, 10)
fmt.Println("Make Slice:", makeSlice)
// Use the range keyword for iteration
for index, value := range slice {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}
// Be mindful of underlying array modifications
modifySlice(slice)
fmt.Println("Modified Slice:", slice)
// Avoid unnecessary copying
newSlice := make([]int, len(slice))
copy(newSlice, slice) // Creating an independent copy of the slice
fmt.Println("New Slice:", newSlice)
// Use array when size is fixed and known
array := [3]int{1, 2, 3}
fmt.Println("Array:", array)
}
func modifySlice(slice []int) {
slice[0] = 10
}
Output
Slice: [1 2 3 4]
Preferred Slice: [Apple Banana Orange]
Make Slice: [0 0 0 0 0]
Index: 0, Value: 1
Index: 1, Value: 2
Index: 2, Value: 3
Modified Slice: [10 2 3 4]
New Slice: [10 2 3 4]
Array: [1 2 3]
In this example:
- The first part demonstrates using slices when flexibility is needed. The slice is dynamically sized, and we append an element to it using append().
- The second part showcases using slices over arrays. The preferredSlice is a slice rather than an array.
- The third part demonstrates initializing a slice with make(). The makeSlice is a slice with a length of 5 and a capacity of 10.
- The fourth part shows using the range keyword for iteration over the slice.
- The fifth part emphasizes being mindful of underlying array modifications by modifying the slice in a function.
- The sixth part illustrates avoiding unnecessary copying by creating an independent copy of the slice using copy().
- The last part highlights using an array when the size is fixed and known.
Conclusion
In conclusion, arrays and slices are fundamental data types in Go for working with collections of elements. While arrays have a fixed size determined at compile-time, slices provide dynamic sizing and additional functionality.
It’s important to choose between arrays and slices based on your specific requirements. Arrays are suitable for scenarios where a fixed-size collection is needed, providing better performance and memory efficiency. On the other hand, slices offer flexibility with dynamic sizing, convenient functions, and the ability to modify the length.
Go Beginner Tutorial Series
- Go Part 1: Setup and Introduction
- GO PART 2: UNDERSTANDING BASIC DATA TYPES AND VARIABLES IN GO
- Go PART 3: OPERATORS AND EXPRESSIONS IN Go
- GO PART 4: CONTROL FLOW STATEMENTS IN GO
- Go PART 5: Functions In Go
- GO PART 6: ARRAYS AND SLICES IN GO
- Go Part 7: String Manipulation In Go
- Go Part 8: Struct Type in Go
- Go Part 9: Pointers in Go
- Go Part 10: Error Handling in Go