Go Arrays and Slices

In Go, arrays are collections of several elements of the same type. Arrays are user-defined data types. Notice we do not use the keyword array to define an array type; instead, we define the type [n]T as an array of n values of type T.

Arrays

array

Array Elements


Simple declaration:

Next expression declares a variable "numbers" as an array of ten (10) integers.

var numbers [10]int

Note: An array's length is part of its type, so arrays cannot be resized. This seems limiting, but don't worry; Go provides a convenient way of working with arrays.

Array Declaration & Initializers

//file array_init.go
package main
import "fmt"

func main() {
    // declare an array with two elements
    var a [2]string

    a[0] = "Hello" // first element
    a[1] = "World" // second element

    fmt.Println(a[0], a[1])
    fmt.Println(a)

    // declare array with initializers
    primes := [6]int{2, 3, 5, 7, 11, 13}
    fmt.Println(primes)
}

Slices

A slice is a dynamically-sized, flexible view into the elements of an array. In practice, slices are much more common than arrays.

The type []T is a slice with elements of type T.

In the next example, we define an array "primes" then we create a slice "s" from element 1 (inclusive) to element 4 (exclusive).

//file array_slice.go
package main
import "fmt"

func main() {
    primes := [6]int{2, 3, 5, 7, 11, 13}

    var s []int = primes[1:4]
    fmt.Println(s) // [3 5 7]
}

Note: Remember the element count starts from 0 in Go.

Slice Operations

References

A slice does not store any data, it just describes a section of an underlying array. Changing the elements of a slice modifies the corresponding elements of its underlying array. Other slices that share the same underlying array will see those changes.

//file array_references.go
package main
import "fmt"

func main() {
    names := [4]string{"John", "Paul", "George", "Ringo"}
    fmt.Println(names)

    b := names[1:3]
    b[0] = "XXX"
    fmt.Println(b)    
    fmt.Println(names)
}

Default Boundary

When slicing, you may omit the high or low bounds to use their defaults instead. The default is zero for the low bound and the length of the slice for the high bound.

For the array: var a [10]int, the following slice expressions are equivalent:

a[0:10]
a[:10]
a[0:]
a[:]

In the next example, we use slice defaults to create 3 slices from the same array:

//file default_boundary.go
package main
import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}

    s = s[1:4]
    fmt.Println(s) // [3 5 7]

    s = s[:2]
    fmt.Println(s) // [3 5]

    s = s[1:]
    fmt.Println(s) // [5]
}

Slice Length and Capacity

A slice has both a length and a capacity. The length is the number of elements it contains. The capacity is the number of elements in the underlying array, counting from the first element in the slice.

//file slice_cap.go
package main
import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    s = s[:0] // Slice to zero length
    printSlice(s)

    s = s[:4] // Extend length
    printSlice(s)

    s = s[2:] // Drop first two values
    printSlice(s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]

Dynamic Growth

Nil Slices

The zero value of a slice is nil. A nil slice has a length and capacity of 0 and has no underlying array.

//file nil_slice.go
package main
import "fmt"

func main() {
    var s []int
    fmt.Println(s, len(s), cap(s))
    if s == nil {
        fmt.Println("nil!")
    }
}

Make Function

Slices can be created with the built-in make() function; this is how you create dynamically-sized arrays. It allocates a zeroed array and returns a slice that refers to it.

//file slice_make.go
package main
import "fmt"

func main() {
    a := make([]int, 5) // len=5
    b := make([]int, 0, 5) // len=0, cap=5
    c := b[:2]
    d := c[2:5]
    
    fmt.Printf("a len=%d cap=%d %v\n", len(a), cap(a), a)
}

Append Function

Go provides a built-in append function to add new elements to a slice.

func append(s []T, vs ...T) []T
//file slice_append.go
package main
import "fmt"

func main() {
    var s []int
    s = append(s, 0)
    s = append(s, 1)
    s = append(s, 2, 3, 4)
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

Range

The range is used to iterate over a slice. The first value is the index, and the second is a copy of the element.

//file range_scan.go
package main
import "fmt"

func main() {
    pow := []int{1, 2, 4, 8, 16, 32, 64, 128}
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }
}