Golang Arrays

Golang Arrays

Hey there!

This blog is all about arrays in Golang. I've already made an intro kind of thing where I explained a little bit about the arrays of Golang. Now we'll dig a bit deep into arrays.

Array

An array is a contiguous storage data structure, which means, it stores elements sequentially in memory. By now I assume you know how indexing works, let's see how elements store in memory.

For example: var arr = [12]int{11, 9, 17, 89, 1, 90, 19, 5, 3, 23, 43, 99}

The above representation says that, if the first element 11 is stored at the memory location 200 then the second element 9 is stored at 204 memory location, and so on.

You might be wondering if 11 is stored at 200 why not 9 starts at 201 instead of 204, because if you can remember an int would typically take 4 bytes (assuming int32). That's why the memory locations are multiples of 4. If we use int64 then the memory locations would look like 200, 208, 216, 224, etc.

You might again be thinking why 11 stored at 200 memory location where it could have started from the beginning, like 1 location or 0 location. To be honest 200 is just to represent some memory location, in practice, it is random, execution engine of a language takes care of these operations like memory allocation and deallocation.

One more important note, arrays can only store homogeneous elements, meaning, elements of same type. If we declare the type to be int, we can not store other types of data.

package main 
import "fmt"

func main(){
    var someArray [2]int = [2]int{100, "John"} // raises an error
}

Various ways to Declare and initialize arrays

Array Literal

package main 
import "fmt"

func main(){
    var arr [2]int = [2]int{100,200}
    fmt.Println(arr)  // prints [100 200]
}

The above declaration is called array literal as we are assigning the values right away when we are declaring the variable.

Declare and Initialize later

package main 
import "fmt"

func main(){
    var arr [2]int 
    arr[0] = 10
    arr[1] = 20
    fmt.Println(arr[0])  // prints 10
}

The above code says that declare a variable arr with type int and of size 2, [2]int

Later, we're assigning the array's 0th index to 10 and 1st index to 20 .

Remember, when we're declaring var arr [2]int, the compiler is creating a arr variable and setting up the size 2 for that variable, and assigning the default value 0 (int default value is 0) to the whole size of that array.

package main 
import "fmt"

func main(){
    var arr [2]int 
    fmt.Println(arr)     // prints [0 0]
}

Initialize with ellipses

package main 
import "fmt"

func main(){
    arr := [...]int{10, 20}
    fmt.Println(arr)     // prints [10 20]
}

Instead of declaring its size, we could use three dots ... known as ellipses. The compiler would know the size of the array by the number of elements provided while declaring it.

Methods of Arrays

  • len() --> returns the size of an array(number of elements in array)

  • cap() --> returns the capacity of an array(number of elements it can hold)

As the size of an array is static i.e fixed, so both len() and cap() are gonna be same.

package main 
import "fmt"

func main(){
    arr := [5]int{10, 20, 30, 40, 50}
    fmt.Println(len(arr))     // prints 2
    fmt.Println(cap(arr))     // prints 2

    var arr2 [10]int
    fmt.Println(arr2)         // prints [0 0 0 0 0 0 0 0 0 0]
    fmt.Println(len(arr))     // prints 10
}

Copying arrays

To copy arrays we would assign one array variable to other.

package main 
import "fmt"

func main(){
    arr := [5]int{10, 20, 30, 40, 50}
    arr2 := arr
    fmt.Println(arr2)    // prints [10 20 30 40 50]
}

The above copying is done as pass by value. Whatever elements are there in arr have been copied to arr2. We can even copy using pass by reference technique.

package main 
import "fmt"

func main(){
    arr := [5]int{10, 20, 30, 40, 50}
    arr2 := &arr
    fmt.Println(*arr2)    // prints [10 20 30 40 50]

    arr2[0] = 96
    fmt.Println(*arr2)   // prints [96 20 30 40 50]
    fmt.Println(arr)    // prints [96 20 30 40 50]
}

If you've noticed &arr, the symbol & is known as ampersand, used to refer to the actual address of the variable rather than value. So, the new variable arr2 wiil still refers to the values of arr. Now modifications made to arr2 will be reflected in arr too.

Note: Variables arr2 is now a pointer, in order to get the values in a pointer variable, we use *, to deference and get the values.

Iterating over array

We can iterate(traverse) over the array elements in order to access elements. For this, we use for loop

package main 
import "fmt"

func main(){
    arr := [3]int{10, 20, 30}
    for i := 0; i<len(arr); i++ {
        fmt.Println(arr[i])
    }
}

// output
10 
20 
30

We can also use range keyword.

package main 
import "fmt"

func main(){
    arr := [3]int{10, 20, 30}
    for i, ele := range arr {
        fmt.Println(i,ele)
    }
}

// output
0 10
1 20
2 30

As you can see in the output, the range keyword returns us both the index and element. If we don't care about index, we can just use undersore _ which ignores that variable.

Array slicing

Slicing basically means cutting and getting the required portion of an array. Let's say we have an arrayarr := [5]int{1, 2, 3, 4, 5}, and we want to get the first 3 elements 1, 2, 3 , in this case, we can iterate over the array and get the three elements. But with slicing, that could be done in a single statement/expression

arr := variable[start:end(excluded)]

package main 
import "fmt"

func main(){
    arr := [5]int{1, 2, 3, 4, 5}
    arr2 := arr[0:3]
    //   0        3 
    // [ 1, 2, 3, 4, 5]
    fmt.Println(arr2)   // prints [1 2 3]
}

Just like we access elements of the array using the index, this time we would provide two indexes, a starting index and an ending index. The elements ranging over these indexes will be returned as a slice.

If you've noticed, the last element 4, which is at index 3 is not returned, because the ending index is excluded. Thus returning [1 2 3] and we stored them in arr2 variable.

There are default values for both the start and end index, if not provided.

The start index defaults to 0, and the end index defaults to the length of an array

package main 
import "fmt"

func main(){
    arr := [5]int{1, 2, 3, 4, 5}
    arr2 := arr[3:]
    //            3     
    // [ 1, 2, 3, 4, 5]
    fmt.Println(arr2)   // prints [4 5]

    arr3 := arr[:2]
    //         2   
    // [ 1, 2, 3, 4, 5]
    fmt.Println(arr3)   // prints [1 2]

    arr4 := arr[:]
    fmt.Println(arr4)   // prints [1 2 3 4 5]
}

As I mentioned, when we slice an array arr2 := arr[3:], we get a slice, not an array.

What is a slice? you may ask. A slice is like an array but doesn't get limited by its size like an array.

Learn more about slices https://mohanj.hashnode.dev/golang-slices

That's it. Will add more details if found anything worth including. Till then, peace out!