Go: Collection types
Table of Contents
- 1. Arrays
- 2. Slices
- 2.1. Slicing a slice
- 2.2. Making slices
- 2.3. Appending to a slice
- 2.4. Length
- 2.5. Nil slices
- 2.6. Copy
- 2.7. Cut
- 2.8. Delete
- 2.9. Delete without preserving order
- 2.10. Cut
- 2.11. Delete
- 2.12. Delete without preserving order
- 2.13. Expand
- 2.14. Extend
- 2.15. Pop
- 2.16. Pop Back
- 2.17. Push
- 2.18. Push Front
- 2.19. Shift
- 2.20. Reversing
- 3. Range
- 4. Maps
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