Go Slice 切片
定義
Go的slice不單單是切片動作,同時也是一種**數據結構
**。Go中的slice依賴於array,它的底層就是數組。
Slice的類型規範是[] T,Slice類型沒有指定的長度
Slice實現
ptr:指向slice開頭的元素
len:slice的長度,用[index]只能取長度內的值
cap:從ptr開始到array結束的整個長度,不超過這個cap都可以擴展
func main() {
arr := [0,1,2,3,4,5,6,7]
s1 := arr[2:6]
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
s2 := s1[3:5]
fmt.Printf("s2=%v len(s2)=%d cap(s2)=%d", s2, len(s2), cap(s2))
}
#output
s1=[100 3 4 5] len(s1)=4 cap(s1)=6
s2=[5 6] len(s2)=2 cap(s2)=3
Slice聲明與創建
slice聲明,沒有初始化的slice位nil slice
var s []int
slice的創建可以是用make,直接賦值或直接從數組中創建切片
· 空slice
var e_s = make([]int,0)
· make
聲明一個length爲3,Capacity爲5,數據類型爲int的底層數組,然後取index 0,1,2,三個元素作爲slice的結構。因爲數組make初始化的關係,fmt.Println(s) => [0 , 0 , 0]
s := make([]int,3,5)
· 直接賦值
s := []int{1,2,3,4}
· 從數組中創建切片
遵循半開半閉原則
arr := [...]int{1, 2, 2, 3, 4, 5, 6}
s := arr[2:6] //半開半閉,2包括,6不包括
// s = [2,3,4,5]
Slice的訪問
slice的訪問和數組差不多,也可以使用下標或range訪問。
使用下標訪問時,只能訪問小於length的下標。
Slice是引用類型
slice可以理解爲從一個底層數組中,選取一些元素,然後返回這個元素的集合,最後用slice指向集合的第一個元素。也可以理解爲,slice其實是一個指針,指向了底層數組中某些元素的集合。
func updateSlice(s []int){
s[0] = 100
}
func main() {
arr := [...]int{1, 2, 2, 3, 4, 5, 6}
fmt.Println("array[2:6] = ", arr[2:6])
fmt.Println("array[:6] = ", arr[:6])
s1 := arr[2:]
fmt.Println("array[2:] = ", s1)
s2 := arr[:]
fmt.Println("array[:] = ", s2)
updateSlice(s1)
fmt.Println("s1 after update = ", s1)
updateSlice(s2)
fmt.Println("s2 after update = ", s2)
}
#output
array[2:6] = [2 3 4 5]
array[:6] = [0 1 2 3 4 5]
array[2:] = [2 3 4 5 6 7]
array[:] = [0 1 2 3 4 5 6 7]
s1 after update = [100 3 4 5 6 7]
s2 after update = [100 1 100 3 4 5 6 7]
Slice的ReSlice
對slice做slice是可以的,生成的slice都是對同一個數組的view。
切片不會複製切片,會創建一個指向原始數組的新切片值。
func main() {
arr := [...]int{1, 2, 2, 3, 4, 5, 6}
s1 := arr[2:]
fmt.Println("s1 = ", s1)
s1 = arr[3:]
fmt.Println("s1[1:3] = ", s1)
}
#output
s1 = [100 3 4 5 6 7]
s1[1:3] = [3 4]
Slice的擴展
· 可以通過appen()來對slice擴展。
· slice可以向後擴展,但不可以向前擴展
· s[i]不能超過len(s),向後擴展不可以超越底層數數組cap(s),這裏指地是通過取下標方式,可參考下面例子。如果append()超過會生成一新的底層數組,返回給新的slice。
· 當底層數組需要擴容時,僅當Capacity需要擴容的時候,會按照當前底層數組長度的2倍進行擴容,並生成新數組。如果底層數組的長度超過1000時,將按照25%的比率擴容,也就是1000個元素時,將擴展爲1250個。
func main() {
oldSlice := []int{1, 2, 3, 4, 5}
newSlice := append(oldSlice, 6)
fmt.Println(oldSlice)
fmt.Println(newSlice)
fmt.Printf("oldSlice=%v len(oldSlice)=%d cap(oldSlice)=%d\n", oldSlice, len(oldSlice), cap(oldSlice))
fmt.Printf("newSlice=%v len(newSlice)=%d cap(newSlice)=%d\n", newSlice, len(newSlice), cap(newSlice))
}
#output
[1 2 3 4 5]
[1 2 3 4 5 6]
oldSlice=[1 2 3 4 5] len(oldSlice)=5 cap(oldSlice)=5
newSlice=[1 2 3 4 5 6] len(newSlice)=6 cap(newSlice)=10
延伸思考1:例子中s2是否會因爲長度不夠而報錯?
arr := [...]int{0,1,2,3,4,5,6,7}
s1 := arr[2:6]
s2 := s1[3:5]
答案是不會的,根據slice實現及下圖可以這麼理解。
s1取了arr[2:6]的view,所以s1的下標就是0,1,2,3,但是因爲s1是arr的view,所以不會到3就結束,會一直到arr結束的下標,在s1中就是5。雖然s1[4]和s1[5]是取不出元素的,但是s1知道底層有這麼個元素。
s2=s1[3:5],同理,s2的下標就有0,1,2,那0,1對的是s1的3,4,即對應到arr的下標就是5,6,取出的值就是5,6。
如果定義s2 = s1[3:5:2],s2就不會知道下標2對應的元素
延伸思考2:下面例子的append() 會影響什麼?
s1 := []int{1,2,3,4,5}
s1_slice := s1[1:3]
s2 := append(s1_slice, 44)
答案是append之後s1[3] = 44,理由如圖:
主要原因還是因爲,底層數組指向的還是同一個。s1_slice取index=2是沒有值的,但是append之後下標2就變成了44。那因爲下標2指向的是底層數組的下標3,所以s1[3]=44。
Slice的Copy
函數copy在兩個slice間賦值數據,賦值長度以len小爲準,兩個silce可指向同一底層數組
func main() {
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
v1 := data[8:] //{8, 9}
v2 := data[:5] //{0, 1, 2, 3, 4}
copy(v2, v1) // dst:s2, src:s1
fmt.Println(v2) //[8 9 2 3 4]
fmt.Println(data) //[8 9 2 3 4 5 6 7 8 9]
}