[Golang] Array and Slice
- 👍 SliceTricks @ Golang Wiki
- Go Slices: usage and internals @ golang
- How to use slice capacity and length in Go @ calhoun
在 Go 裡面有兩種資料結構可以處理包含許多元素的清單資料,分別是 Array 和 Slice:
- Array:清單的長度是固定的(fixed length),屬於原生型別(primitive type),較少在程式中使用。
- Slice:可以增加或減少清單的長度,使用
[]
定義,例如,[]byte
是 byte slice,指元素為 byte 的 slice;[]string
是 string slice,指元素為 string 的 slice。
不論是 Array 或 Slice,它們內部的元素都必須要有相同的資料型別(字串、數值、...)。
這裡的「陣列」一般指稱的是可變動長度的 Slice。
TL;DR
- slice 背後的
array
不夠放時,append 後會建立一個新的 array - slice 就像窗戶(window),只能透過這個窗戶看到部分的 array
- 複製 slice,或從某一個 slice 產生新的 slice,概念就像是產生了一個新的窗戶
Arrays 的建立
基本
在 Array 中陣列的元素是固定的:
// 陣列型別:[n]T
// 陣列的元素只能有 10 個,且都是數值
func main() {
// 方式一:先定義再賦值
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a) // [Hello World]
// 方式二
var people [4]int // len=4 cap=4,[0,0,0,0]
people = [4]int{10,20,30,40} // len=4 cap=4,[10,20,30,40]
// 方式三:定義且同時賦值
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes) // [2 3 5 7 11 13]
}
使用 ... 根據元素數目建立 Array
使用 [...]T{}
可以根據元素的數目自動建立陣列:
// 沒有使用 ...,建立出來的會是 slice
arr := []string{"North", "East", "South", "West"}
fmt.Println(reflect.TypeOf(arr).Kind(), len(arr)) // slice 4
// 使用 ...,建立出來的會是 array
arrWithDots := [...]string{"North", "East", "South", "West"}
fmt.Println(reflect.TypeOf(arrWithDots).Kind(), len(arrWithDots)) // array 4
How do you determine if a variable is a slice or array? @ StackOverflow
Slice 的建立
基本
- slice 實際上只是一個能夠指稱到底層 array 的 pointer
- 在思考的時候,要把 slice 當成一個遮罩或視窗(window),透過它過濾原本 array 的內容後,看到的內容才是 slice 的結果
// slice 型別:[]T
// slice := make([]T, len, cap)
func main() {
// 方式一:建立一個帶有資料的 string slice,適合用在知道 slice 裡面的元素有哪些時
people := []string{"Aaron", "Jim", "Bob", "Ken"}
// 方式二:透過 make 可以建立「空 slice」,適合用會對 slice 中特定位置元素進行操作時
people := make([]int, 4) // len=4 cap=4,[0,0,0,0]
// 方式三:空的 slice,一般會搭配 append 使用
var people []string
// 方式四:大該知道需要多少元素時使用,搭配 append 使用
people := make([]string, 0, 5) // len=0 cap=5, []
}
實際上當我們在 Go 建立 slice 時,它內部會建立兩個不同的資料結構,分別是 slice 和 array。
- slice 的 zero value 是
nil
,而nil
的 slice 其len
和cap
都是0
。
根據 Array 產生 Slice
可以把 Slice 想成 Window,實際上看到的東西還是 Array,但透過 Slice 這個 window 只能看到部分的 Array
arr := [5]int{1, 2, 3, 4, 5}
// 根據這個 Array 產生 Slice
sliceVersion := arr[:]
fmt.Println(sliceVersion) // [1 2 3 4 5]
partialSlice := arr[2:5]
fmt.Println(partialSlice) // [3 4 5]
提示
slice 只存了能夠連結到底層 array 的 pointer,在思考的時候,要把 "slice" 當成一個遮罩或視窗(window),透過它過濾原本 array 的內容後,看到的內容才是 slice 的結果。
capacity and length
在 Slice 中會包含
- Pointer to Array:這個 pointer 會指向實際上在底層的 array。
- Capacity:從 slice 的第一個元素開始算起,它底層 array 的元素數目
- Length:該 slice 中的元素數目
它們會如下圖存放在記憶體中,因此當我們使用該 slice 時,它會先指向該 slice,而該 slice 中的 pointer to head 會在指到底層的 array: