切片(slice)
slice 又稱動態陣列,依託陣列實現,可以方便地進行擴容和傳遞,實際使用時比陣列更靈活。
初始化
- 宣告變數
- 變數值都為零值,對於切片來講零值為 nil
- 字面量
- 空切片是指長度為空,而值不是 nil
- 宣告長度為0的切片時,推薦使用變數宣告的方式獲得一個 nil 切片,而不是空切片,因為 nil 切片不需要記憶體分配
- 內建函式make()
- 推薦指定長度同時指定預估空間,可有效地減少切片擴容時記憶體分配及拷貝次數
- 切取
- 切片與原陣列或切片共享底層空間
// 1. 宣告變數
var s []int // nil 切片
// 2. 字面量
s1 := []int{} // 空切片
s2 := []int{1, 2, 3} // 長度為3的切片
// 3. 內建函式 make()
s3 := make([]int, 12) // 指定長度
s4 := make([]int, 12, 100) // 指定長度和空間
// 4. 切取
arr := [5]int{1, 2, 3, 4, 5}
s5 := arr[2:4] // 從陣列中切取
s6 ;= s5[1:2] // 從切片中切取
操作
append()
當切片空間不足時,append()
會先建立新的大容量切片,新增元素後再返回新切片。
擴容規則
- 原 slice 的容量小於 1024,則新 slice 的容量將擴大為原來的 2 倍
- 原 slice 的容量大於或等於 1024,則新 slice 的容量將擴大為原來的 1.25 倍
append()
向 slice 新增一個元素的實現步驟如下:
- 加入 slice 的容量夠用,則將新元素追加進去,slice.len++,返回 slice
- 原 slice 的容量不夠,則將 slice 先擴容,擴容後得到新 slice
- 將新元素追加進新 slice, slice.len++,返回新的 slice
s := make([]int, 0)
s = append(s, 1) // 新增1個元素
s = append(s, 2, 3, 4 ,5) // 新增多個元素
s = append(s, []int{6, 7}...) //新增一個切片
len()
和 cap()
由於切片的本質為結構體,結構體中儲存了切片的長度和容量,所以這兩個操作的時間複雜度均為 O(1)
copy()
會將源切片的資料逐個拷貝到目的切片指向的陣列中,拷貝數量取兩個切片長度的最小值。
例如長度為 10 的切片拷貝到長度為 5 的切片中時,將拷貝 5 個元素
也就是說,拷貝過程中不會發生擴容。
實現原理
src/runtime/slice.go:slice
type slice struct {
array unsafe.Pointer
len int
cap int
}
array 指標指向底層陣列,len 表示切片長度,cap 表示陣列容量
切片表示式
- 簡單表示式 s[low : high]
- 擴充套件表示式 s[low : high : max]
使用簡單表示式生成的切片將與原陣列或切片共享底層陣列。新切片的生成邏輯可以使用一下虛擬碼表示:
b.array = &a[low]
b.len = high - low
b.cap = len(a) - low
// 擴充套件表示式中的 max 用於限制新生成切片的容量,新切片的容量為 max - low
b.cap = max - low
如果切片表示式發生越界就觸發 panic
切取 string
作用於字串時會則會產生新的字串,而不是切片
擴充套件表示式不能用於切取 string
本作品採用《CC 協議》,轉載必須註明作者和本文連結