Slice(切片)- 《Go 專家程式設計》筆記提要

Uptutu發表於2020-11-12

切片(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 協議》,轉載必須註明作者和本文連結

相關文章