跳至主要内容

Go 語言的集合型別

· 閱讀時間約 5 分鐘
Ckai

集合型別 Collection Types

若說 int, rune, byte, bool 等儲存單一值的型別為核心型別,
能儲存多個值的陣列(array)、切片(slice)、映射表(map)就可稱為集合型別。

Array

先宣告空陣列再賦值
// var 陣列名稱 [陣列容量]儲存型別
var array0 [10]int
fmt.Println(array0) // [0 0 0 0 0 0 0 0 0 0]

// 已宣告陣列 = [陣列容量]儲存型別{值1, 值2, 值3, ...}
array0 = [10]int{
1, 2, 3, 4, 5,
}
fmt.Println(array0) // [1 2 3 4 5 0 0 0 0 0]
陣列容量可用 ... 表示,其容量取決於賦值時給定的值的數量。
// 陣列名稱 := [...]儲存型別{值1, 值2, 值3, ...}
array1 := [...]string{"一", "二", "三"} // 給定三個值,所以 array1 的容量為 3。
fmt.Println(array1) // [一 二 三]
容量與型別相同的陣列才可比較
array2 := [5]int{1, 2, 3, 4, 0}
array3 := [...]int{1, 2, 3, 4, 0}
array4 := [5]int{1, 2, 3, 4, 5}
array5 := [4]int{1, 2, 3, 4}

fmt.Println(array2 == array3) // true
fmt.Println(array2 == array4) // false

// invalid operation: array2 == array5 (mismatched types [5]int and [4]int)
fmt.Println(array2 == array5)
備註

只有 array 能用上述方法比較。slice 或 map 必須用迴圈遍歷各個集合,
然後逐一比對其值,才能確定是否相等。

Slice

切片底下有個「隱藏陣列」,用以儲存資料。

切片的三項隱藏屬性

  • 指標 pointer(指向隱藏陣列起始位置)
  • 長度 length(切片中的元素數量)
  • 容量 capacity(切片可容納的元素數量)

內建函式 len() 與 cap()

  • len() 可檢視切片長度
  • cap() 可檢視切片容量
宣告切片並賦值,然後調用 len 與 cap 函式。
// 切片變數名稱 := []儲存資料型別{值1, 值2, ...}
slice1 := []bool{true, false, false}
fmt.Println(slice1) // [true false false]
fmt.Println(len(slice1)) // 3
fmt.Println(cap(slice1)) // 3
利用 make 函式建立切片,然後調用 len 與 cap 函式。
slice2 := make([]int, 3, 5)
fmt.Println(slice2) // [0 0 0]
fmt.Println(len(slice2)) // 3
fmt.Println(cap(slice2)) // 5

切片是指向隱藏陣列的指標

切片的本質就是「指向隱藏陣列的指標」。因此複製切片也可說是「複製指向隱藏陣列的指標」。
下列代碼中,三個陣列都是指向同一隱藏陣列的指標。

複製切片會產生指向同一隱藏陣列的指標
func LinkedSlices() (int, int, int) {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1 // 直接複製切片(指向同一陣列)
slice3 := slice1[:] // 從切片建立長度相同的切片(指向同一陣列)

slice1[3] = 0
return slice1[3], slice2[3], slice3[3] // 0, 0, 0
}

所謂「切片的容量增加」,實際上是「隱藏陣列的容量增加」。
然而同一陣列的容量是不可增加的,真正的底層運作原理是會新增一個容量較大的陣列, 並將切片轉而指向新陣列,使得結果像是隱藏陣列的容量增加。

func NoLinkSlices() (int, int) {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1

// slice1 的容量為 5,欲加入新值就必須建立新的陣列,
// 因此下一行代碼執行後,slice1 會指向為擴充容量而新建的陣列。
slice1 = append(slice1, 6)

slice1[3] = 0 // 此時 slice1 與 slice2 分別指向不同陣列
return slice1[3], slice2[3] // 0, 4
}