今晚閑來無事,總結(jié)一下Go的slice
slice是什么
slice在Go中的原型?
slice類似數(shù)組,是一種定長的數(shù)組。在底層slice是這樣的:
type slice struct {
array unsafe.Pointer
len int
cap int
}
如何創(chuàng)建slice
1.創(chuàng)建nil slice
var lst []int
2.創(chuàng)建無容量的空slice
lst := make([]int, 0, 0)
3.創(chuàng)建有容量的空slice
lst := make([]int, 0, 6)
4.創(chuàng)建零值slice(10個0)
lst := make([]int, 10)
5.用引用創(chuàng)建slice
lst := array[:]
slice的其他操作函數(shù)
len(lst) 獲得slice的長度
cap(lst) 獲得slice的容量
append(lst, 6) 給lst添加1個數(shù)字6
append(lst, lst2…) 給lst添加一個slice,lst2是一個slice
copy(lst, srcList) //拷貝srcList中內(nèi)容到lst中去,僅能拷貝len(lst)個元素
部分細節(jié)陷阱
1.函數(shù)形參為slice
函數(shù)形參傳遞slice是值傳遞,并不是引用傳遞,在函數(shù)中修改形參的值,并不會影響函數(shù)外的傳入的slice
解決辦法
(1):傳入slice指針
func modify(lst *[]int) {
//修改lst
}
(2):函數(shù)內(nèi)修改了slice后返回
func modify(lst []int) []int {
//修改lst
return s
}
(3):將函數(shù)作為slice的指針
type sliceInt []int
func(this *sliceInt) modify() {
//修改lst
}
2.數(shù)據(jù)同源
func main() {
lst := [2]int{}
lst[0] = 1
fmt.Printf("lst: %v , len: %d, cap: %d\n", lst, len(lst), cap(lst))
dlst := lst[0:1]
dlst[0] = 5
fmt.Printf("lst: %v ,len: %d, cap: %d\n", lst, len(lst), cap(lst))
fmt.Printf("dlst: %v, len: %d, cap: %d\n", dlst, len(dlst), cap(dlst))
}
輸出如下
lst: [1 0] , len: 2, cap: 2
lst: [5 0] ,len: 2, cap: 2
dlst: [5], len: 1, cap: 2
原因:dlst會指向來源于lst,底層數(shù)據(jù)指針是指向lst的,修改dlst的數(shù)據(jù)也同時會修改lst的數(shù)據(jù)。
3.slice擴容陷阱
func main() {
lst := [2]int{}
lst[0] = 1
fmt.Printf("lst: %v , len: %d, cap: %d\n", lst, len(lst), cap(lst))
dlst := lst[0:1]
dlst = append(dlst, []int{2, 3}...)
dlst[1] = 1
fmt.Printf("lst: %v ,len: %d, cap: %d\n", lst, len(lst), cap(lst))
fmt.Printf("dlst: %v, len: %d, cap: %d\n", dlst, len(dlst), cap(dlst))
}
輸出如下:
lst: [1 0] , len: 2, cap: 2
lst: [1 0] ,len: 2, cap: 2
dlst: [1 1 3], len: 3, cap: 4
原因:因為存儲空間不夠,需要擴容,dlst會被分配新的空間,所以此時修改dlst數(shù)據(jù)并不會改變lst中的源數(shù)據(jù)。因為dlst數(shù)據(jù)指針已經(jīng)不再指向源數(shù)據(jù)地址。
Empty Slice和Nil Slice和零slice
1.零slice
lst := make([]int, 10)
10個零值slice
2.空Slice
lst := make([]int, 0)
沒有長度和容量的slice
3.nil Slice
var lst []int
此時lst是一個nil的slice,底層數(shù)據(jù) len:0,cap:0,*Elem:nil
注:可以對nil的slice 使用append操作。
擴容策略
append的擴容規(guī)則為:
1.如果新申請容量大于2倍的舊容量,最終容量就是新申請的容量;否則執(zhí)行第2步
2.如果舊切片的長度小于1024,則最終容量就是舊容量的兩倍,否則執(zhí)行第3步
3.如果舊切片長度大于等于1024,則最終容量從舊容量開始循環(huán)增加原來的 1/4,即直到最終容量大于等于新申請的容量
4.如果最終容量(cap)計算值溢出,則最終容量(cap)就是新申請容量(cap)
注:具體的代碼參考go目錄 src/runtime/slice.go中的growslice函數(shù)。
文章也同步發(fā)在了個人網(wǎng)站上
http://www.seabytelab.com/go-slice-lite.html