Go基礎 Slice

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]
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章