Slice
Slice 是可變長度的 Array ,其元素的型別皆相同。slice 的型別寫作 []T 。
array 與 slice 緊密相關。slice 可存取底層陣列的部分或全部元素。
slice 有三個元件:
- 指標(指向底層陣列,代表 slice 的第一個元素。但不一定是陣列的第一個元素)
- 長度(該 slice 長度,但長度不會超過 slice 開頭到底層陣列最尾端的長度)
- 容量(slice 開頭的元素到底層陣列最尾端的長度)
舉例來說
months := [...]string{1: "January", 2: "February", 3: "March", 4: "April", 5: "May", 6: "June", 7: "July", 8: "August", 9: "September", 10: "October", 11: "November", 12: "December"}
Q2 := months[4:7]
summer := months[6:9]
fmt.Println(Q2) // ["April", "May", "June"]
fmt.Println(summer) // ["June", "July", "August"]
Slice 的運算子 s[i:j]的意思是從底層陣列的第 i 項取到 j-1 項 依上面的例子來說,Q2 就是從 months 第 4 項取到第 6 項 也有些特殊的寫法 s[i:] 意思是從第 i 項取到最後一項 s[:j] 意思是從頭取到第 j - 1 項 s[:] 意思是取全部元素 所以 months[3:] 就會從三月取到十二月
要注意的是如果 slice 超過底層陣列的容量(cap)會造成 panic 承上例如果 summer slice 的範圍超過 months 就會出現 panic
summer := months[:20] // panic
然後,跟 array 不同的地方。因為 slice 的元素中帶有指標。所以我們把 slice 傳給函式(function)的時候。他是會修改到底層陣列的實例的。
func reverse(s []int){
for i, j := 0, len(s)-1; i < j; i, j = i + 1, j - 1 {
s[i], s[j] = s[j], s[i]
}
}
a := [...]int{0, 1, 2, 3, 4, 5}
reverse(a[:])
fmt.Println(a) // [5, 4, 3, 2, 1]
還有一點需要特別注意兩個 Slice 之間是不能使用 == 比較的 標準函式庫中,有 bytes.Equal 來比較兩個 []byte 之間的差別,但其他的型態我們需要自己實作。
唯一合法的 slice 比較是與 nil 互相比較。 如果你要判斷 slice 是否為空請使用 len(s) == 0 來判斷
var s []int // len(s) == 0, s == nil
s = nil // len(s) == 0, s == nil
s = []int(nil) // len(s) == 0, s == nil
s = []int{} // len(s) == 0, s != nil
append function
我們來看看 slice append 的簡單概念
func appendInt(x []int, y int) []int{
var z []int
zlen := len(x) + 1
if zlen <= cap(x){
z = x[:zlen]
} else {
zcap := zlen
if zcap < 2*len(x){
zcap = 2 * len(x)
}
z = make([]int, zlen, zcap)
copy(z, x)
}
z[len(x)] = y
return z
}
實際上的 append function 用的是更複雜的策略,但這個簡單的 appendInt 是一個很好的概念。 由上面的 function 你可以清楚的知道 當一個 slice 要加入新的元素時,他會先 check 底層陣列的容量夠不夠。如果夠就擴大 slice 的長度,再把元素加到最後面。 如果不夠,那就建立一個兩倍容量的底層陣列。然後把現有的複製過去,再放入元素。