什么是Go語言的切片
在Go語言中,切片(Slice)是一種基于數組的動態數據結構。切片提供了一種方便且靈活的方式來操作變長序列的元素。切片擴展了數組的概念,它可以動態分配和擴容,而不需要像數組一樣提前聲明固定的長度。
切片由三個部分組成:
-
指針(Pointer):指向底層數組的起始位置。
-
長度(Length):表示切片當前包含的元素個數。
-
容量(Capacity):表示底層數組從切片起始位置到結束位置的總元素個數。
切片的特點:
-
切片是引用類型,可以像數組一樣傳遞和賦值給其他變量。
-
切片可以使用索引訪問和修改元素。
-
切片的長度可以動態改變,它會自動擴容以容納更多的元素。
-
切片的容量會根據擴容機制自動調整,但可以通過預先指定切片的容量來優化性能。
-
切片可以使用內置的Append()函數在末尾添加元素。
切片的增刪改查
-
增加元素:可以使用內置的append()函數來在切尾添加一個或多個元素。示例:
slice := []int{1, 2, 3} slice = append(slice, 4) // 添加一個元素 slice = append(slice, 5, 6) // 添加多個元素
-
刪除元素:可以使用切片的切片操作或者copy()函數來刪除指定位置的元素。示例:
-
切片的切片操作:
slice := []int{1, 2, 3, 4, 5} slice = append(slice[:2], slice[3:]...) // 刪除索引為2的元素
-
copy()函數刪除元素:
slice := []int{1, 2, 3, 4, 5} index := 2 slice = append(slice[:index], slice[index+1:]...) // 刪除索引為2的元素
-
-
修改元素:可以通過索引直接修改切片中的元素。示例:
slice := []int{1, 2, 3, 4, 5} slice[2] = 6 // 將索引為2的元素修改為6
-
查找元素:可以通過遍歷切片或使用內置的range關鍵字來查找元素。示例:
-
遍歷切片:
slice := []int{1, 2, 3, 4, 5} for i := 0; i < len(slice); i++ { if slice[i] == 3 { // 找到了索引為2的元素 break } }
-
使用range關鍵字:
slice := []int{1, 2, 3, 4, 5} for index, value := range slice { if value == 3 { // 找到了索引為2的元素 break } }
-
切片如何進行擴容
Go語言切片的擴容機制是通過append()函數來實現的。
當切片容量不夠時,Go語言會自動擴容切片。具體的擴容機制如下:
-
判斷切片是否需要擴容:
-
如果切片的長度小于其容量,則無需擴容。
-
如果切片的長度等于其容量,則需要擴容。
-
-
計算新的切片容量:
-
如果原切片的容量小于1024,則新的切片容量將擴大為原切片容量的2倍。
-
如果原切片的容量大于等于1024,則新的切片容量將擴大為原切片容量的1.25倍。
-
-
創建新的底層數組:
-
根據新的切片容量,創建一個新的底層數組,長度為新的切片容量。
-
-
將原切片中的元素復制到新的底層數組中。
-
將新的底層數組作為切片的底層數組,更新切片的指針、長度和容量。
-
返回新的切片。
這種機制可以有效地減少內存的浪費,提高了切片的使用效率。同時,這種擴容機制也會導致切片在添加元素時可能會觸發內存重新分配和復制,因此在需要頻繁添加元素的場景下,建議使用預先指定切片容量的方式初始化切片,以避免頻繁的擴容操作。
源碼介紹
type slice struct {
array unsafe.Pointer
len int
cap int
}
切片擴容大概源碼
// growslice 函數 handle slice growth in append.
func growslice(et *_type, old slice, cap int) slice {
// 根據現有的切片長度和容量,計算新的切片容量
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
for newcap < cap {
newcap += newcap / 4
}
}
}
// 分配新的數組空間
var overflow bool
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
newcap, capmem, overflow = newmmap(et.size, newcap, capmem, overflow)
if overflow || newcap < cap {
panic(errorString("growslice: cap out of range"))
}
// 創建新的切片,并復制舊的切片中的數據
var p unsafe.Pointer
p = mallocgc(capmem, et, true)
var newcap int
newcap = int(capmem / et.size)
return slice{p, old.len, newcap}
}