Go Array & Slice

Gary Liao
5 min readMay 25, 2023

--

建議搭配官方介紹Slice的文件

宣告

Array & Slice 的宣告方式如下:

var array1 [5]int                           // [0 0 0 0 0]
var slice0 []int // []
var slice1 []int = make([]int, 5, 5) // [0 0 0 0 0]
slice11 := new([5]int)[0:5] // [0 0 0 0 0]

var array2 [5]int = [5]int{0, 1, 2, 3, 4} // [0 1 2 3 4]
var slice2 []int = []int{0, 1, 2, 3, 4} // [0 1 2 3 4]

array3 := [5]int{0, 1, 2, 3, 4} // [0 1 2 3 4]
slice3 := []int{0, 1, 2, 3, 4} // [0 1 2 3 4]

array4 := [...]int{0, 1, 2, 3, 4} // [0 1 2 3 4]
slice4 := []int{0, 1, 2, 3, 4} // [0 1 2 3 4]

array5 := [...]int{3: -1, -1} // [0 0 0 -1 -1]
slice5 := []int{3: -1, -1} // [0 0 0 -1 -1]

len & cap

 var array [5]int
slice := make([]int, 5, 7)
fmt.Println(len(array) == 5) // true
fmt.Println(cap(array) == 5) // true
fmt.Println(len(slice) == 5) // true
fmt.Println(cap(slice) == 7) // true

Array 是固定長度的,slice是基於array的輕量struct,可變動長度。

Array 與 slice 都有 len & cap 這兩個屬性。len 是表面上的長度,cap 則是 memory allocate 的長度,Array 的這兩個屬性都是固定的,slice則都可改。

“Slicing” an existing slice or array

透過 [a:b] 的語法,可以在 "表面上" 擷取 array 或 slice 的片段,並且生成一個新的 slice,但底層的 array 並沒有改變。

slice6 := array1[:]
fmt.Printf("%T %v len:%d cap:%d \n", array1, array1, len(array1), cap(array1))
// [5]int [0 0 0 0 0] len:5 cap:5
fmt.Printf("%T %v len:%d cap:%d \n", slice6, slice6, len(slice6), cap(slice6))
// []int [0 0 0 0 0] len:5 cap:5

型別

Array的長度不同,型別就不同,無法互相做布林運算:

fmt.Println([2]int{1, 2} == [3]int{1, 2, 3})
// error:
// invalid operation: [2]int{…} == [3]int{…}
// (mismatched types [2]int and [3]int)

但畢竟是親兄弟,相同長度且元素型別相同的 array 與 slice 之間可以做布林運算:

fmt.Println([2]int{1, 2} == [...]int{1, 2}) // true

傳值? 傳址?

Array 在傳入 function 時,會傳入一份複製,所以在 function 內不會改動到原本 array 的值;若傳入array的指標則可以。

Slice 在傳入 function時,可以改動原本slice的值。

package main

import "fmt"

func mutateArray(x [5]int) {
x[2] = 5
}
func mutateArrayByAddr(x *[5]int) {
(*x)[2] = 5
}
func mutateSlice(x []int) {
x[2] = 5
}

func main() {

var array1 [5]int // [0 0 0 0 0]
mutateArray(array1)
fmt.Println("array1", array1) // array1 [0 0 0 0 0]
mutateArrayByAddr(&array1)
fmt.Println("array1", array1) // array1 [0 0 0 0 0]


var slice1 []int = make([]int, 5, 5) // [0 0 0 0 0]
mutateSlice(slice1)
fmt.Println("slice1", slice1) // slice1 [0 0 5 0 0]
}

make([]T, length, capacity)

slice 的初始化。

append(s S, x ...E) S // core type of S is []E

附加 slice。

(需要更多探討)

copy(dst, src []T) int

將 slice 的值複製到另一個記憶體空間。

garbage collection

根據 slice-intro 這份官方文件:

… slice references the original array, as long as the slice is kept around the garbage collector can’t release the array …

也就是說,如果 slice 背後的 array 很肥大 ( cap 很大 ),即便 slice 只用到一小段 ( len 很小 ),garbage collector 依然不會動這個背後的 array。

解決方式: 使用 make 創一個len與cap都合適的slice,再從肥大的slice把需要的部分 copy 到新的slice。

--

--

Gary Liao
Gary Liao

Written by Gary Liao

我很忙,一秒鐘幾十萬上下。

No responses yet