dcsimg
March 3, 2021
Hot Topics:

Arrays and Slices in Go

  • By Manoj Debnath
  • Send Email »
  • More Articles »

Basic type declarations such as integers, floating-point numbers, boolean, strings, and constants form the basic building blocks of any programming language. The Go language also includes complex types to accommodate real and imaginary components, each of which are nothing but float32 and float64 types, respectively. Arrays and slices are two of the most common composite types to represent data in Go. Here we’ll discuss these two types with examples.

Arrays in Go

Go arrays are fixed length data structures of zero or more elements of a particular type stored in memory. The elements are characterized by their homogeneity, which simply means the same type of n number of elements can be stored in an array. The functionalities with arrays in Go are quite similar to arrays in C/C++. There is an indexing operator [] for declaring an array and the values start at the 0 position. Therefore, the array [0] indicates the first element and array[len(array)-1] indicates the last element. Note that there is a built-in versatile function called len() that returns the size of the type (in this case array size). The len() function can also be used to get the size of a string literal or any other composite types such as slices or map.

As with arrays, it is possible to create both one dimensional or multidimensional arrays where the access values always start with indexes 0 to its size-1. This means that if the size of the array is 10, then the starting index value would be array [0] and the last value would be at array[len(array)-1]. Arrays are mutable; if we put array[index] at the left of an assignment it indicates that we are assigning some value to the array at the location indicated by the index. Similarly, if we put array[index] at the right of the assignment it indicates that we want to get the array value at the specific index location.

Some valid syntaxes for creating an array can be illustrated with the following code.

var intArray [5]int
fmt.Printf("%T %d %v", intArray, len(intArray), intArray)

The output is:

[5]int 5 [0 0 0 0 0]

Note: Details about formatted I/O such as what does %T, %d etc. mean in Go can be found in the Documentation.

In the code above we have declared an integer array of size 5 and print in the standard output its type, length and the value it contains. Note that we have not initialized the array but the output shows that it has been already initialized with the value 0.

Interestingly, we can create an array using the ellipse operator (...)  as follows.

strArray := [...]string{"Mercury", "Venus", "Earth", "Mars"}
fmt.Printf("\n%T %d %q", strArray, len(strArray), strArray)

When we use ellipse, operator Go internally calculates the array size. The operator has been overloaded for the purpose. The size is statically counted and does not in any way mean that it creates a dynamic array.

A few things to note about arrays

  • An array declared is array initialized, this means that Go guarantees that array items if not explicitly initialized, partly or fully, then it automatically initializes it to their zero values at the time of creation.

  • Array length can be retrieved by the len() function. Since arrays have a fixed-length, which is the same as its capacity, the cap() function returns the same result as the len() function.

  • Arrays are passed by value to functions, which incurs an additional cost due to copy. However, a way around is passing pointers to the array.

Array Example

Here is a quick implementation of a matrix multiplication using an array.

package main

import "fmt"

func main() {
A := [3][3]int{
{1, 1, 0},
{1, 1, 1},
{1, 0, 0},
}
B := [3][3]int{
{0, 1, 0},
{1, 0, 1},
{1, 0, 1},
}
var C [3][3]int
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
C[i][j] = C[i][j] + A[i][k]*B[k][j]
}
}
}
fmt.Println("A =", A)
fmt.Println("B =", B)
fmt.Println("C =", C)
}

Which yields the output:

A = [[1 1 0] [1 1 1] [1 0 0]]
B = [[0 1 0] [1 0 1] [1 0 1]]
C = [[1 1 1] [2 1 2] [0 1 0]]

Slices in Go

Slices are more versatile than arrays in Go. They are more flexible to operate on. Unlike arrays, slices are dynamic in nature in the sense that here the length of the array is determined during creation. We can use the built-in make function to create a slice. Slices are comparatively more efficient while passing as a function argument because they are always passed as a reference. The length of a slice can be resized after creation while arrays are of fixed length. All standard API libraries internally use slices rather than array. Although slices store items of the same type, with the use of interface we can store items of any type in an interesting way.

A few things to note about slices

  • The size of the slice can be extended or reduced after creation. It can be shrunk by slicing them or extended by built-in function append().

  • Slice length can be retrieved by the len() function.

  • Slices are passed by reference.

  • Built-in make function can be used to create a slice.

Slice Example

package main

import (
"fmt"
"math/rand"
)

func main() {

randInt := make([]int, 3)

//insert some random data
for i := 0; i < 3; i++ {
randInt[i] = rand.Intn(100)
}

for i := 0; i < 3; i++ {
fmt.Printf("%d\t", randInt[i])
}

}

Matrix multiplication using slices

Here is a quick implementation of a matrix multiplication using slices.

package main

import "fmt"

func main() {

A := [][]int{
{1, 1, 0},
{1, 1, 1},
{1, 0, 0},
}
B := [][]int{
{0, 1, 0},
{1, 0, 1},
{1, 0, 1},
}
C := make([][]int, 3)
for i := range C {
C[i] = make([]int, 3)
}

for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
C[i][j] = C[i][j] + A[i][k]*B[k][j]
}
}
}

fmt.Println("A =", A)
fmt.Println("B =", B)
fmt.Println("C =", C)
}

Yielding the output:

A = [[1 1 0] [1 1 1] [1 0 0]]
B = [[0 1 0] [1 0 1] [1 0 1]]
C = [[1 1 1] [2 1 2] [0 1 0]]

Conclusion

Both arrays and slices are typically used to store items of the same type. Arrays are characterized by its rigidity in their implementation. Once created the size remains fixed, and they are passed by value to a function. However, we may use pointers if we are interested in passing arrays by reference. Slices are always passed by reference to functions. They are more flexible and can be easily manipulated to increase or decrease their size at runtime. Slices can be thought of as an array of hidden size and are built to overcome the shortcomings of an array. They have very similar purposes but slice is recommended to be used more often than arrays due to its versatility. Their semantics are the same, syntaxes are equivalent but one’s flexibility far outweighs the other. Arrays are static in nature whereas slices are more dynamic. These are the key differences between them from the point of view of implementation in Go program.






This article was originally published on February 18, 2021

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.


Thanks for your registration, follow us on our social networks to keep up-to-date