Arrays and Slices in Go

Posted drvongoosewing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Arrays and Slices in Go相关的知识,希望对你有一定的参考价值。

Introduction

Roughly speaking, there are two kinds of types in Go. Non-collections and collections. Non-collections, like Boolean, Interger, Floats, and so on. We have discussed those types in "Primitive Types in Go". In that article, when we talked about text(strings and runes), we actually have used some kinds of collection types. Because string is collection of letters. Today, we will go deeper into collection types. Our main job is understanding two important collection types: Arrays and Slices.

In this article we will talk about:

1. Creation

2. Build-in functions

3. Working with

 

Array

0. Usage(aka. Why do we need array?)

For example:

func main() {
	grade1 := 90
	grade2 := 87
	grade3 := 79
	fmt.Printf("Grades: %v, %v, %v", grade1, grade2, grade3)
}
// output
// Grades: 90, 87, 79

There are two problems in this simple case:

1.  As the code goes long, it will be hard to find how many grades there are. 

2. Working with many individual variables are cumbersome than working with a collection. (Can‘t use for-loop)

1. Creation

First, write the size of our array in brackets, like [3]. Second, write the type of our data, like [3]string or [6]int. In Go and many other languages, an array can only stores one type of data.

What‘s more, data in an array will be stored in an continous section of momory, so it will be more quick than individual variables.

func main() {
	var grades [3]int = [3]int{90, 86, 88}
	fmt.Printf("Grades: %v", grades)
}
// output
// Grades: [90 86 88]

This code works fine. But we write [3]int two times. To make things simpler, we can write as:

func main() {
	grades := [3]int{90, 86, 88}
     //grades := [...]int{90, 86, 88} fmt.Printf("Grades: %v", grades) } // output // Grades: [90 86 88]

Further we can write [...] to instead of the size number inside. If our array is long, we can use this method to avoid counting number by ourself. Especcially when we need redesign the code and change the list inside the array.

Some people say style like [...]int{90, 86, 88} will make a slice, they are wrong. Let‘s remember it is an array.

2. Build-in functions

There is a function len() to return how long an array is.

func main() {
	grades := [3]int{90, 86, 88}
	fmt.Println(len(grades))
}
// output
// 3

3. Wroking with(acess, two dimention array, copy)

We can use square brackets with index number to call and assign an element. In the result we can see, Go‘s array output doesn‘t have "," to seperate each element.

func main() {
	var students [3]string
	fmt.Printf("Students: %v
", students)
	students[0] = "Lisa"
	students[2] = "Ahmed"
	students[1] = "Arnold"
	fmt.Printf("Students: %v
", students)
	fmt.Printf("Students #1 %v
", students[0])
}
// output
//Students: [  ]
//Students: [Lisa Arnold Ahmed]
//Students #1 Lisa

Two dimention array is most used, for matrix or dataframe.

func main() {
	var matrix [3][3]int
	matrix[0] = [3]int {1, 0, 0}
	matrix[1] = [3]int {0, 1, 0}
	matrix[2] = [3]int {0, 0, 1}
	fmt.Printf("Matrix: %v", matrix)

	matrix2 := [3][3]int {[3]int {1, 0, 0}, [3]int {0, 1, 0}, [3]int {0, 0, 1}}
	fmt.Printf("
Matrix2: %v", matrix2)
}
// output
//Matrix: [[1 0 0] [0 1 0] [0 0 1]]
//Matrix2: [[1 0 0] [0 1 0] [0 0 1]]

Go‘s array has an important difference with other language. That is Go use array like a value. If we pass an array to another variable. Go will do a value-copy(copy), not a pointer-copy(view).

This may cause time complex problem when our array is very big. But we can use & operator to do pointer-copy(view) by ourself.

func main() {
	a := [3]int{90, 80, 70}
	b := a
	b[0] = 99
	fmt.Println(a)
	fmt.Println(b)

	fmt.Println("----------")
	c := &a 
	c[0] = 99
	fmt.Println(a)
	fmt.Println(c)
	fmt.Println(*c)
}
// output
[90 80 70]
[99 80 70]
----------
[99 80 70]
&[99 80 70]
[99 80 70]

 

Slice

0. Usage(Why do we need slice?)

Array is powerful, however they have fixed size. This may limite usefulness. So in Go we have other thing called slice.
We can think slice is an upgrade version of array. Most things we can do with an array, we can do with a slice.

1. creation

Declare a slice looks like declare an array, but we don‘t write size in the bracket.
func main() {
	a := []int {90, 80, 70}
	fmt.Println(a)
}
// output
// [90 80 70]
 
There are serveal other ways we can create slices. The slicing operation [] can work in slice, and it can also work in array. Slicing on a array will result a slice. They will share same memory address(view).
func main() {
	a := [10]int {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	b := a[:]
	c := a[3:]
	d := a[:6]
	e := a[3:6]
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)
	fmt.Println(e)
	fmt.Println("----------")
	a[5] = 99
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)
	fmt.Println(e)
}
// output
[1 2 3 4 5 6 7 8 9 10]
[1 2 3 4 5 6 7 8 9 10]
[4 5 6 7 8 9 10]
[1 2 3 4 5 6]
[4 5 6]
----------
[1 2 3 4 5 99 7 8 9 10]
[1 2 3 4 5 99 7 8 9 10]
[4 5 99 7 8 9 10]
[1 2 3 4 5 99]
[4 5 99]
The last method we can make a slice is using build-in make() function.
make() function takes 2 or 3 arguments. The first argument is type, like []int. The second argument is the length of the slice. The third argument, if we will, is the capacity. Capacity doesn‘t have to equal length.
By the way, make() function can not create an array.
func main() {
	a := make([]int, 3)
	fmt.Println(a)
	fmt.Printf("Length: %v
", len(a))
	fmt.Printf("Capacity: %v
", cap(a))

	b := make([]int, 3, 100)
	fmt.Println(b)
	fmt.Printf("Length: %v
", len(b))
	fmt.Printf("Capacity: %v
", cap(b))
}
// output
[0 0 0]
Length: 3
Capacity: 3
[0 0 0]
Length: 3
Capacity: 100
The reason we may need a capacity argument is that slice doesn‘t have a fix size for its entire life.  But Go has to assure our slice stored in continous memory.
This makes each time we append or delete an element in the slice, Go has to copy the original slice, then assigns it with the new element to the new memory address. This may be a time complex problem when slice is large.
What‘s more, each time we append new element to a slice, Go will check if capacity is enough. If it‘s enouch, it just append. if it‘s not enough, Go will double original capacity.
But if we can assure the slice will be some capacity at top, we can tell Go in make() and Go doesn‘t have to copy/ assign/ check/ double capacity so frequently.
Instead, Go can open a continous memory at the beginning with your appoint capacity.
func main() {
	a := []int {}
	fmt.Println(a)
	fmt.Printf("Length: %v
", len(a))
	fmt.Printf("Capacity: %v
", cap(a))
	
	fmt.Println("----------")
	a = append(a, 1)
	fmt.Println(a)
	fmt.Printf("Length: %v
", len(a))
	fmt.Printf("Capacity: %v
", cap(a))

	fmt.Println("----------")
	a = append(a, 2)
	fmt.Println(a)
	fmt.Printf("Length: %v
", len(a))
	fmt.Printf("Capacity: %v
", cap(a))

	fmt.Println("----------")
	a = append(a, 3)
	fmt.Println(a)
	fmt.Printf("Length: %v
", len(a))
	fmt.Printf("Capacity: %v
", cap(a))

	fmt.Println("----------")
	a = append(a, 4)
	fmt.Println(a)
	fmt.Printf("Length: %v
", len(a))
	fmt.Printf("Capacity: %v
", cap(a))

	fmt.Println("----------")
	a = append(a, 5)
	fmt.Println(a)
	fmt.Printf("Length: %v
", len(a))
	fmt.Printf("Capacity: %v
", cap(a))
}
// output
[]
Length: 0
Capacity: 0
----------
[1]
Length: 1
Capacity: 2
----------
[1 2]
Length: 2
Capacity: 2
----------
[1 2 3]
Length: 3
Capacity: 4
----------
[1 2 3 4]
Length: 4
Capacity: 4
----------
[1 2 3 4 5]
Length: 5
Capacity: 8

  

 
 

2. Build-in functions

We have already seem how to use make() to create a slice. By the way, make() function can‘t make an array.
cap() is the capacity of the slice. Capacity can be different with length. It is the the longest length possible and length is how long for now.
func main() {
	a := []int {90, 80, 70}
	fmt.Println(a)
	fmt.Printf("Length: %v
", len(a))
	fmt.Printf("Capacity: %v
", cap(a))
}
// output
[90 80 70]
Length: 3
Capacity: 3
Another important build-in function is append(). We can use this function to add elements behind an slice.
One common task of append() is we want to append one slice after another. But unfortunally append() only accepts element, it doesn‘t accept anther slice
There is an operator "..." can help, it will unpack the slice to elements
func main() {
	a := []int{}
	//a = append(a, []int{2, 3, 4}) won‘t work
	a = append(a, []int{2, 3, 4}...)
	fmt.Println(a)
}
// output
[2 3 4]

3. Working with  

We can remember in array, if we just pass an array to other varible, it is passing a copy of array
But in slice, it is not. both varibles have same memory address. That is, slice will always be a view.
func main() {
	a := []int {90, 80, 70}
	b := a
	b[0] = 99
	fmt.Println(a)
	fmt.Println(b)
}
// output
[99 80 70]
[99 80 70]  
Another common task is delete some element in an slice. We can use slicing opeator [:] to delete beginnig and ending
But if we want to delete some in the middle, we have to again use append().
Be ware, slicing is a view of original data.
func main() {
	a := []int{1, 2, 3, 4, 5}
	fmt.Printf("a is %v
", a)
     // delete first element b := a[1:]
     // delete last element, a[:-1] won‘t work in Go. c := a[:len(a)-1] fmt.Printf("b is %v ", b) fmt.Printf("c is %v ", c) fmt.Printf("now, a is %v ", a) fmt.Println("----------")
     // delete the middle element, that is, value 3 d := append(a[:2], a[3:]...) // [1 2] append 4 and 5 fmt.Printf("d is %v ", d) fmt.Printf("now, a is %v ", a) // [1 2 4 5 5] fmt.Println("----------")
     // if we want to delete value 3, but not affect original one a = []int{1, 2, 3, 4, 5} e := []int{} for _, num := range a { if num != 3 { e = append(e, num) } } fmt.Printf("e is %v ", e) fmt.Printf("now, a is %v", a) } // output a is [1 2 3 4 5] b is [2 3 4 5] c is [1 2 3 4] now, a is [1 2 3 4 5] ---------- d is [1 2 4 5] now, a is [1 2 4 5 5] ---------- e is [1 2 4 5] now, a is [1 2 3 4 5]

  

Summary

Arrays

- Collections of items of same type

- Fixed size

- Declaration styles:

  - a := [3]int{1, 2, 3}

  - a := [...]int{1, 2, 3}

  - var a [3]int

- Access with zero-based index
  - a := [3]int{1, 2, 3} // a[1] == 2
- len() function returns size of array
- Copies refer to different underlying data

Slices

- Backed by array
- Creations styles:
  - Slice existing array or slice
  - Literal style
  - Via make() function
    a := make([]int, 10) // slice with length and capacity 10
    a := make([]int, 10, 100) // slice with length 10 and capacity 100
- len() function returns length of slice
- cap() function returns length of underlying array
- append() function to add elements to slice
- May cause expensive copy operation if our underlying array(capacity) is small
- Try our best to use make() and specify capacity to let underlying array big enough
- Copies refer to same underlying array

 

 

 

 

  

以上是关于Arrays and Slices in Go的主要内容,如果未能解决你的问题,请参考以下文章

Go Slices:用法和内部结构

go map and slice 2021-10-08

[Go] Slices vs Array

go语言合并两个数组

Go 语言入门很简单 -- 7. Go Slices #私藏项目实操分享#

Slices in Golang