UP | HOME

Go: Collection types

Table of Contents

1 Arrays

The type [n]T is an array of n values of type T. An array's length is part of its type, so array cannot be resized.

package main

import "fmt"

func main() {
        var a1[10]int;
        fmt.Printf("1. %q\n", a1)
        //
        var a2 [2]string
        a2[0] = "Hello"
        a2[1] = "World"
        fmt.Println("2.", a2[0], a2[1])
        fmt.Println("3.", a2)
        // set the array entries as you declare the array
        a3  := [2]string{"hello", "world!"}
        fmt.Printf("4. %q\n", a3)
        // use an ellipsis to use an implicit length when you pass the values
        a4 := [...]string{"hello", "world!"}
        fmt.Printf("5. %q\n", a4)
}
1. ['\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00' '\x00']
2. Hello World
3. [Hello World]
4. ["hello" "world!"]
5. ["hello" "world!"]

1.1 Printing arrays

package main

import "fmt"

func main() {
        a := [2]string{"hello", "world!"}
        fmt.Println(a)
        // [hello world!]
        fmt.Printf("%s\n", a)
        // [hello world!]
        fmt.Printf("%q\n", a)*
        // ["hello" "world!"]
}

1.2 Multi-dimensional arrays

package main

import "fmt"

func main() {
        var a [2][3]string
        for i := 0; i < 2; i++ {
                for j := 0; j < 3; j++ {
                        a[i][j] = fmt.Sprintf("row %d - column %d", i+1, j+1)
                }
        }
        fmt.Printf("%q", a)
}

[["row 1 - column 1" "row 1 - column 2" "row 1 - column 3"] ["row 2 - column 1" "row 2 - column 2" "row 2 - column 3"]]

2 Slices

Slices hold references to an underlying array, and if you assign one slice to another, both refer to the same array. If a function takes a slice argument, changes it makes to the elements of the slice will be visible to the caller, analogous to passing a pointer to the underlying array. A slice points to an array of values and also includes a length. Slices can be resized since they are just a wrapper on top of another data structure. []T is a slice with elements of type T

package main

import "fmt"

func main() {
        p := []int{2, 3, 5, 7, 11, 13}
        fmt.Println(p)
}
[2 3 5 7 11 13]

2.1 Slicing a slice

Slices can be re-sliced, creating a new slice value that points to the same array. Thus, s[lo:hi] evaluates to a slice of the elements from lo through hi-1, inclusive. s[lo:lo] evaluates empty slice. s[lo:lo+1] has one element.

package main

import "fmt"

func main() {
        mySlice := []int{2, 3, 5, 7, 11, 13}
        fmt.Println(mySlice)

        fmt.Println(mySlice[1:4])

        // missing low index implies 0
        fmt.Println(mySlice[:3])

        // missing high index implies len(s)
        fmt.Println(mySlice[4:])
}
[2 3 5 7 11 13]
[3 5 7]
[2 3 5]
[11 13]

2.2 Making slices

Besides creating slices by passing the values right away (slice literal), you can also use make. It works by allocating a zeroed array and returning a slice that refers to that array.

package main

import "fmt"

func main() {
        cities := make([]string, 3)
        cities[0] = "Santa Monica"
        cities[1] = "Venice"
        cities[2] = "Los Angeles"
        fmt.Printf("%q", cities)
}
["Santa Monica" "Venice" "Los Angeles"]

When you make multi-dimensional slice, it will make slice with N empty slices

package main

import "fmt"

func main() {
        a := make([]int, 5)
        printSlice("a", a)

        b := make([]int, 0, 5)
        printSlice("b", b)

        c := b[:2]
        printSlice("c", c)

        d := c[2:5]
        printSlice("d", d)

        e := make([][]int, 12)
        fmt.Println("multi-dimensional slice e: ", e)
        printSlice("e[0]", e[0])
}

func printSlice(s string, x []int) {
        fmt.Printf("%s len=%d cap=%d %v\n",
                s, len(x), cap(x), x)
}
a len=5 cap=5 [0 0 0 0 0]
b len=0 cap=5 []
c len=2 cap=5 [0 0]
d len=3 cap=3 [0 0 0]
multi-dimensional slice e:  [[] [] [] [] [] [] [] [] [] [] [] []]
e[0] len=0 cap=0 []

2.3 Appending to a slice

You would get a runtime error if you were to do that

cities := []string{}
cities[0] = "Santa Monica"

There is a way to do that though, and that is by using the append function:

package main

import "fmt"

func main() {
        cities := []string{}
        cities = append(cities, "San Diego")
        fmt.Println(cities)
        // You can append more than one entry to a slice
        cities = []string{}
        cities = append(cities, "San Diego", "Mountain View")
        fmt.Printf("%q", cities)
        // You can also append a slice to another using an ellipsis
        otherCities := []string{"Santa Monica", "Venice"}
        cities = append(cities, otherCities...)
        fmt.Printf("%q", cities)
}
[San Diego]
["San Diego" "Mountain View"]["San Diego" "Mountain View" "Santa Monica" "Venice"]

ELLIPSIS IS A BUILT-IN FEATURE OF THE LANGUAGE THAT MEANS THAT THE ELEMENT IS A COLLECTION.

2.4 Length

package main

import "fmt"

func main() {
        cities := []string{
                "Santa Monica",
                "San Diego",
                "San Francisco",
        }
        fmt.Println(len(cities))
        countries := make([]string, 42)
        fmt.Println(len(countries))
}
3
42

2.5 Nil slices

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

package main

import "fmt"

func main() {
        var z []int
        fmt.Println(z, len(z), cap(z))

        if z == nil {
                fmt.Println("nil!")
        }
}
[] 0 0
nil!

2.6 Copy

b = make([]T, len(a))
copy(b, a)
// or
b = append([]T(nil), a...)

2.7 Cut

a = append(a[:i], a[j:]...)

2.8 Delete

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

2.9 Delete without preserving order

a[i] = a[len(a)-1]
a = a[:len(a)-1]
package main

import "fmt"

func main() {
        cities := []string{
                "A",
                "B",
                "C",
                "D",
                "E",
        }
        fmt.Println(cities)
        cities[2] = cities[len(cities)-1]
        fmt.Println(cities)
        cities = cities[:len(cities)-1]
        fmt.Println(cities)
}
[A B C D E]
[A B E D E]
[A B E D]

NOTE If the type of the element is a pointer or a struct with pointer fields, which need to be garbage collected, the above implementations of Cut and Delete have a potential memory leak problem: some elements with values are still referenced by slice a and thus can not be collected. The following code can fix this problem.

2.10 Cut

copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
        a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]

2.11 Delete

copy(a[i:], a[i+1:])
a[len(a)-1] = nil // or the zero value of T
a = a[:len(a)-1]
package main

import "fmt"

func main() {
        cities := []string{
                "A",
                "B",
                "C",
                "D",
                "E",
        }
        i := 2
        fmt.Println("BEFORE: ", cities)
        copy(cities[i:], cities[i+1:])
        fmt.Println("AFTER COPY: ", cities)
        cities[len(cities)-1] = "" // or the zero value of T
        fmt.Println("AFTER ERASE: ", cities)
        cities = cities[:len(cities)-1]
        fmt.Println("AFTER RESIZE: ", cities)
}
BEFORE:  [A B C D E]
AFTER COPY:  [A B D E E]
AFTER ERASE:  [A B D E ]
AFTER RESIZE:  [A B D E]

2.12 Delete without preserving order

a[i] = a[len(a)-1]
a[len(a)-1] = nil
a = a[:len(a)-1]

2.13 Expand

package main

import "fmt"

func main() {
        cities := []string{
                "A",
                "B",
                "C",
                "D",
                "E",
        }
        i := 2
        j := 4
        cities = append(cities[:i], append(make([]string, j), cities[i:]...)...)
        fmt.Println(cities)
}

2.14 Extend

a = append(a, make([]T, j)...)

2.15 Pop

x, a = a[0], a[1:]

2.16 Pop Back

x, a = a[len(a)-1], a[:len(a)-1]

2.17 Push

a = append(a, x)

2.18 Push Front

a = append([]T{x}, a...)

2.19 Shift

x, a := a[0], a[1:]

2.20 Reversing

for i := len(a)/2-1; i >= 0; i-- {
        opp := len(a)-1-i
        a[i], a[opp] = a[opp], a[i]
}

3 Range

The range form of the loop iterates over a slice or a map.

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
        for i, v := range pow {
                fmt.Printf("2**%d = %d\n", i, v)
        }
}
2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128

You can skip index or value by assigning to _. If you only want the index, drop the ", value" entirely.

package main

import "fmt"

func main() {
        pow := make([]int, 10)
        for i := range pow {
                pow[i] = 1 << uint(i)
        }
        for _, value := range pow {
                fmt.Printf("%d\n", value)
        }
}
1
2
4
8
16
32
64
128
256
512

3.1 Break & continue

package main

import "fmt"

func main() {
        pow := make([]int, 10)
        for i := range pow {
                pow[i] = 1 << uint(i)
                if pow[i] >= 16 {
                        break
                }
        }
        fmt.Println(pow)

        pow = make([]int, 10)
        for i := range pow {
                if i%2 == 0 {
                        continue
                }
                pow[i] = 1 << uint(i)
        }
        fmt.Println(pow)
}
[1 2 4 8 16 0 0 0 0 0]
[0 2 0 8 0 32 0 128 0 512]

4 Maps

Maps are somewhat similar to what other languages call “dictionaries” or “hashes”. A map maps keys to values.

package main

import "fmt"

func main() {
        celebs := map[string]int{
                "Nicolas Cage":       50,
                "Selena Gomez":       21,
                "Jude Law":           41,
                "Scarlett Johansson": 29,
        }

        fmt.Printf("%#v", celebs)
}
map[string]int{"Nicolas Cage":50, "Selena Gomez":21, "Jude Law":41, "Scarlett Johansson":29}

When not using map literals like above, maps must be created with make (not new) before use. The nil map is empty and cannot be assigned to.

package main

import "fmt"

type Vertex struct {
        Lat, Long float64
}

// nil map
var m1 map[string]Vertex

// map literal
var m2 = map[string]Vertex{
        "Bell Labs": {40.68433, -74.39967},
        // same as "Bell Labs": Vertex{40.68433, -74.39967}
        "Google": {37.42202, -122.08408},
}


func main() {
        m1 = make(map[string]Vertex)
        m1["Bell Labs"] = Vertex{40.68433, -74.39967}
        fmt.Println(m1["Bell Labs"])
        // print map literal
        fmt.Println(m2)
}
{40.68433 -74.39967}
map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]

4.1 Insert an element

m[key] = value

4.2 Delete an element

delete(m, key)

4.3 Retrieve an element

If key is in m, ok is true. If not, ok is false and elem is the zero value for the map’s element type. Similarly, when reading from a map if the key is not present the result is the zero value for the map’s element type.

value, ok = m[key]
// ok is true if key in m, value is zero otherwise
value = m[key]
// value is zero unless key in m

Author: Pavel Vavilin

Created: 2017-10-26 Thu 20:58

Validate