Skip to main content

數組、切片、哈希表

介紹

數組和切片是 Go 語言中常見的數據結構,很多剛使用 Go 的開發者往往會混淆這個概念。 切片和哈希表皆是引用類型,基礎數據和數組是值類型。

數組

數組是由相同類型元素的集合組成的數據結構,計算機會為數組分配一塊連續的內存來保存其中的元素,我們可以利用術組中元素的索引快速訪問特定元素。數組在初始化後大小、元素類型就無法改變。

數組初始化

arr1 := [3]int{1, 2, 3}
arr2 := [...]int{1, 2, 3}

切片

切片,即動態數組,其長度並不固定,我們可以向切片中追加元素,它會在容量不足時自動擴容。 引用傳遞操作

Cache Flow

type slice struct { 
// * 指向起始地址
ptr unsafe.Pointer

// * 切片長度(邏輯長度,存放元素個數)
len int

// * 切片容量(物理長度,分配多少空間)
cap int
}

// * 宣告切片,s是一個 nil 空指針,沒有完成內存分配
var s []int

// * 宣告切片長度、容量為8,初始值0
s := make([]int,8)

// * 宣告切片長度、容量為16,初始值0
s := make([]int,8,16)

// * 宣告切片並初始化,長度、容量為3
var s []int{0,1,5}

切片操作

截取操作

每一次截取切片都會創建新的slice header

Cache Flow

s := []int{1,2,3,4,5,6}

// * [1,2,3]
s1 = s[:3]

// * [2,3]
s2 = s[1:3]

// * [1,2,3,4,5]
s3 = s[:len(s)-1]
引用操作

但是不會對原切片擴容

func TestSlice (t *testing.T) {
s := []int{0,1,2}
// * [0,1,2] -> [10,1,2]
ChangeSlice (s)
}

// * s1 創建 slice header,拷貝 s(ptr, len, cap)
func ChangeSlice (s1 []int) {
s1[0] = 10
// * 不影響原先 s 切片
s1 = append(s1,20)
}
追加元素

擴容時遵循如下機制:

  • 預期新容量超過原容量的兩倍,直接取預期新容量
  • 原容量小於 256,直接取原容量的兩倍作為新容量
  • 原容量大於 256,在原容量 n 基礎上循環執行 n+=(n+3*256)/4 的操作,直到 n 大於等於預期新容量,並取 n 作為新容量
  • 提醒 slice 並不是併發安全的數據結構,使用時注意併發安全
func TestSlice (t *testing.T) {
s := []int{0,1,2}
// * {0,1,2,10},擴容會生成新地址
s = append(s,10)
}

func TestInitSlice (t *testing.T) {
s := make([]int,0,5)
for i := 0; i < 5; i++ {
s = append(s,i)
}
// * [0,1,2,3,4]
}

func TestInitSlice (t *testing.T) {
s := make([]int,5)
for i := 0; i < 5; i++ {
s[i] = i
}
// * [0,1,2,3,4]
}
刪除元素

本質為截取切片

        s := []int{0,1,2,3,4}

// * 刪除 idx 2 元素,[0,1,3,4],len 4,cap 5
s := append(s[:2],s[3:]...)

// * 刪除切片所有元素,len 0,cap 5
s := s[:0]

切片拷貝操作


// * s1/s2 新的 slice header(ptr, len, cap)拷貝,s2 len 少 1,兩個切片操作相同內存空間
s := []int{0,1,2,3,4}
s1 := s
s2 := s[1:]

// * 開闢新內存空間,拷貝元素到新的空間,兩個切片互相獨立
s := []int{0,1,2,3,4}
s1 := make ([]int{}, len(s))
copy (s1, s)

哈希表

map 是一種無序基於key-value的數據結構,Go 是引用類型,必須初始化才能使用。 make 用於 slice、map、channel 初始化

make 創建map

userinfo := make(map[string]string)
userinfo["name"] = "merry"
userinfo["age"] = "20"

make 創建map

programinfo := map[string]string{
"name": "merry",
"age": "20",
}

哈希表操作

查找、創建、更新、刪除

userinfo := make(map[string]string)
userinfo["name"] = "merry"
userinfo["age"] = "20"

v, ok := userinfo["age"]

delete(userinfo, "age")