1. 簡介
在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函式,
2. 使用
slice的使用包括定義、初始化、添加、洗掉、查找等操作,
2.1 slice定義
slice是一個參考型別,可以通過宣告變數并使用make()函式來創建一個slice:
var sliceName []T
sliceName := make([]T, length, capacity)
其中,T代表該切片可以保存的元素型別,length代表預留的元素數量,capacity代表預分配的存盤空間,
2.2 初始化
slice有兩種初始化的方式:宣告時初始化和使用append()函式初始化:
// 宣告時初始化
sliceName := []T{value1, value2, ..., valueN}
// 使用append()函式進行初始化
sliceName := make([]T, 0, capacity)
sliceName = append(sliceName, value1, value2, ..., valueN)
2.3 獲取slice元素
slice中的元素可以通過索引的方式來獲取,與c/c++類似,go的索引也是從0開始的:
sliceName[index]
2.4 添加元素到slice中
可以通過使用append()函式將元素添加到slice中,如果slice的容量不足,則會自動擴展,語法如下:
sliceName = append(sliceName, value1, value2, ..., valueN)
2.5 洗掉slice中的元素
可以使用append()函式和切片操作來從slice中洗掉元素,使用append()函式時,需要將帶有要洗掉元素的切片放在最后,語法如下:
// 通過切片操作洗掉元素
sliceName = append(sliceName[:index], sliceName[index+1:]...)
// 通過append()函式洗掉元素
sliceName = append(sliceName[:index], sliceName[index+1:]...)
如上所見,二者的表現形式是一樣的,但內部實作是不同的:
- 使用append()進行洗掉的方式,實際上是將后面的元素向前移動一個位置,然后通過重新切片的方式來洗掉最后一個元素,這種方式會創建一個新的底層陣列,并將原來的元素復制到新的陣列中,因此在洗掉多個元素時可能會導致記憶體分配和復制開銷較大,影響性能
- 使用切片語法進行洗掉,底層陣列中被洗掉元素的位置仍然存在,但是這些位置不再包含有效的資料,這種方式的性能比使用append()進行洗掉要好,尤其是在洗掉多個元素時,因為它不需要創建新的底層陣列,也不需要復制元素,但是,這種方式可能會導致底層陣列中存在大量未使用的空間,浪費記憶體
需要注意的是,在切片中洗掉元素時,會重新分配記憶體并復制元素,因此洗掉元素的成本會相對較高,為了減少記憶體分配和復制元素的次數,可以使用copy函式將后面的元素復制到前面,然后將切片的長度減少,具體實作方法可以參考下面的:
// 洗掉切片中指定位置的元素
func removeElement(slice []int, index int) []int {
copy(slice[index:], slice[index+1:])
return slice[:len(slice)-1]
}
2.6 查找slice中的元素
可以使用for和range遍歷slice來實作元素查詢:
// 使用for回圈和range關鍵字遍歷Slice
for index, value := range sliceName {
if value =https://www.cnblogs.com/lianshuiwuyi/p/= targetValue {
// 找到了目標元素
break
}
}
2.7 切片操作
可以使用切片操作來獲取子切片,操作如下:
// 切片操作:獲取從第i個元素到第j個元素的子切片
sliceName[i:j]
// 切片操作:獲取從第i個元素到第j個元素,且容量為k的子切片
sliceName[i:j:k]
3. 關于slice擴容
在Go語言中,slice會隨著元素的增加而動態擴容,當容量不足時,slice會自動重新分配記憶體,將原有元素復制到新的底層陣列中,并在新陣列后面添加新的元素,
slice的擴容機制可以描述為:當slice的長度超過了底層陣列的容量時,Go語言會按照一定的策略重新分配一塊更大的記憶體,并將原來的元素復制到新的記憶體中,然后再添加新元素,具體的策略如下:
- 如果新長度(即len(s)+1)小于等于原長度(即cap(s)),則
slice不需要擴容,直接添加元素即可, - 如果新長度大于原長度且小于原長度的兩倍(即 cap(s)*2),則新
slice的容量就是原來的兩倍,也就是說將底層陣列擴容為原來的兩倍,并將原來的元素復制到新的陣列中, - 如果新長度大于原長度的兩倍,會嘗試使用新長度作為容量,如果仍然不夠,則按照擴容倍數(默認是 2)來擴容,
需要注意的是,slice擴容是一個開銷比較大的操作,因為需要重新分配記憶體、復制資料等,所以在撰寫代碼時應該盡可能地減少slice擴容的次數,以提高程式的性能,
宣告:本作品采用署名-非商業性使用-相同方式共享 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請注明出處,
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/551419.html
標籤:Go
上一篇:菜鳥記錄:c語言實作PAT甲級1005--Spell It Right
下一篇:返回列表
