在go中,slice
是一種動態陣列型別,其底層實現中使用了陣列。slice
有以下特點:
*slice
本身並不是陣列,它只是一個參照型別,包含了一個指向底層陣列的指標,以及長度和容量。
*slice
的長度可以動態擴充套件或縮減,通過append
和copy
操作可以增加或刪除slice
中的元素。
*slice
的容量是指在底層陣列中slice
可以繼續擴充套件的長度,容量可以通過make
函數進行設定。
Slice 的底層實現是一個包含了三個欄位的結構體:
type`slice`struct {
ptr uintptr // 指向底層陣列的指標
len int // slice 的長度
cap int // slice 的容量
}
當一個新的slice
被建立時,Go會為其分配一個底層陣列,並且把指向該陣列的指標、長度和容量資訊儲存在slice
結構體中。底層陣列的長度一般會比slice
的容量要大,以便在append
操作時有足夠的空間儲存新元素。
當一個slice
作為引數傳遞給函數時,其實是傳遞了一個指向底層陣列的指標,這也就意味著在函數內部對slice
的修改也會反映到函數外部。
在進行切片操作時,slice 的指標和長度資訊不會發生變化,只有容量資訊會發生變化。如果切片操作的結果仍然是一個 slice,那麼它所參照的底層陣列仍然和原來的slice
是同一個陣列。
需要注意的是,當一個slice
被傳遞給一個新的變數或者作為引數傳遞給函數時,並不會複製底層陣列,而是會共用底層陣列。因此,如果對一個slice
的元素進行修改,可能會影響到共用底層陣列的其他slice
。如果需要複製一個slice
,可以使用copy
函數。
slice
的使用包括定義、初始化、新增、刪除、查詢等操作。
slice
是一個參照型別,可以通過宣告變數並使用make()函數來建立一個slice
:
var sliceName []T
sliceName := make([]T, length, capacity)
其中,T代表該切片可以儲存的元素型別,length代表預留的元素數量,capacity代表預分配的儲存空間。
slice
有兩種初始化的方式:宣告時初始化和使用append()函數初始化:
// 宣告時初始化
sliceName := []T{value1, value2, ..., valueN}
// 使用append()函數進行初始化
sliceName := make([]T, 0, capacity)
sliceName = append(sliceName, value1, value2, ..., valueN)
slice
中的元素可以通過索引的方式來獲取,與c/c++類似,go的索引也是從0開始的:
sliceName[index]
可以通過使用append()函數將元素新增到slice
中。如果slice
的容量不足,則會自動擴充套件。語法如下:
sliceName = append(sliceName, value1, value2, ..., valueN)
可以使用append()函數和切片操作來從slice
中刪除元素。使用append()函數時,需要將帶有要刪除元素的切片放在最後。語法如下:
// 通過切片操作刪除元素
sliceName = append(sliceName[:index], sliceName[index+1:]...)
// 通過append()函數刪除元素
sliceName = append(sliceName[:index], sliceName[index+1:]...)
如上所見,二者的表現形式是一樣的,但內部實現是不同的:
需要注意的是,在切片中刪除元素時,會重新分配記憶體並複製元素,因此刪除元素的成本會相對較高。為了減少記憶體分配和複製元素的次數,可以使用copy
函數將後面的元素複製到前面,然後將切片的長度減少。具體實現方法可以參考下面的:
// 刪除切片中指定位置的元素
func removeElement(slice []int, index int) []int {
copy(slice[index:], slice[index+1:])
return slice[:len(slice)-1]
}
可以使用for和range遍歷slice
來實現元素查詢:
// 使用for迴圈和range關鍵字遍歷Slice
for index, value := range sliceName {
if value == targetValue {
// 找到了目標元素
break
}
}
可以使用切片操作來獲取子切片,操作如下:
// 切片操作:獲取從第i個元素到第j個元素的子切片
sliceName[i:j]
// 切片操作:獲取從第i個元素到第j個元素,且容量為k的子切片
sliceName[i:j:k]
在Go語言中,slice
會隨著元素的增加而動態擴容。當容量不足時,slice
會自動重新分配記憶體,將原有元素複製到新的底層陣列中,並在新陣列後面新增新的元素。
slice
的擴容機制可以描述為:當slice
的長度超過了底層陣列的容量時,Go語言會按照一定的策略重新分配一塊更大的記憶體,並將原來的元素複製到新的記憶體中,然後再新增新元素。具體的策略如下:
slice
不需要擴容,直接新增元素即可。slice
的容量就是原來的兩倍,也就是說將底層陣列擴容為原來的兩倍,並將原來的元素複製到新的陣列中。需要注意的是,slice
擴容是一個開銷比較大的操作,因為需要重新分配記憶體、複製資料等。所以在編寫程式碼時應該儘可能地減少slice
擴容的次數,以提高程式的效能。
宣告:本作品採用署名-非商業性使用-相同方式共用 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請註明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意