您好,登錄后才能下訂單哦!
在項目開發過程中,更多的場景是需要一個長度可以動態更新的數據存儲結構,切片本身并非是動態數組或數組指針,他內部通過指針引用底層數組,并設定相關屬性將數據讀寫操作限定在指定區域內。比如:
/runtime/slice.go type slice struct { array unsafe.Pointer len int cap int }
切片初始化
切片有兩種基本初始化方式:
切片可以通過內置的make函數來初始化,初始化時len=cap,一般使用時省略cap參數,默認和len參數相同,在追加元素時,如果容量cap不足時,將按len的2倍動態擴容。
通過數組來初始化切片,以開始和結束索引位置來確定最終所引用的數組片段。
//make([]T, len, cap) //T是切片的數據的類型,len表示length,cap表示capacity { s := make([]int,5) //len: 5 cap: 5 s := make([]int,5,10) //len: 5 cap: 10 s := []int{1,2,3} } { arr := [...]int{0,1,2,3,4,5,6,7,8,9} s1 := arr[:] s2 := arr[2:5] s3 := arr[2:5:7] s4 := arr[4:] s5 := arr[:4] s6 := arr[:4:6] fmt.Println("s1: ",s1, len(s1),cap(s1)) fmt.Println("s2: ",s2, len(s2),cap(s2)) fmt.Println("s3: ",s3, len(s3),cap(s3)) fmt.Println("s4: ",s4, len(s4),cap(s4)) fmt.Println("s5: ",s5, len(s5),cap(s5)) fmt.Println("s6: ",s6, len(s6),cap(s6)) } 輸出: s1: [0 1 2 3 4 5 6 7 8 9] 10 10 s2: [2 3 4] 3 8 s3: [2 3 4] 3 5 s4: [4 5 6 7 8 9] 6 6 s5: [0 1 2 3] 4 10 s6: [0 1 2 3] 4 6
通過上例說明cap 是表示切片所引用數組片段的真實長度,len是表示已經賦過值的最大下標(索引)值加1.
注意下面兩種初始化方式的區別:
{ var a []int b := []int{} fmt.Println(a==nil,b==nil) fmt.Printf("a: %#v\n", (*reflect.SliceHeader)(unsafe.Pointer(&a))) fmt.Printf("b: %#v\n", (*reflect.SliceHeader)(unsafe.Pointer(&b))) fmt.Printf("a size %d\n", unsafe.Sizeof(a)) fmt.Printf("b size %d\n", unsafe.Sizeof(b)) } 輸出: true false a: &reflect.SliceHeader{Data:0x0, Len:0, Cap:0} b: &reflect.SliceHeader{Data:0x5168b0, Len:0, Cap:0} a size 24 b size 24 說明: 1. 變量b的內部指針被賦值,即使該指針指向了runtime.zerobase,但它依然完成了初始化操作 2. 變量a表示一個未初始化的切片對象,切片本身依然會分配所需的內存
切片之間不支持邏輯運算符,僅能判斷是否為nil,比如:
{ var a []int b := []int{} fmt.Println(a==b) //invalid operation: a == b (slice can only be compared to nil) }
reslice
在原slice的基礎上進行新建slice,新建的slice依舊指向原底層數組,新創建的slice不能超出原slice
的容量,但是不受其長度限制,并且如果修改新建slice的值,對所有關聯的切片都有影響,比如:
{ s := []string{"a","b","c","d","e","f","g"} s1 := s[1:3] //b,c fmt.Println(s1, len(s1),cap(s1)) s1_1 := s1[2:5] //c,d,e fmt.Println(s1_1, len(s1_1),cap(s1_1)) } 輸出: [b c] 2 6 [d e f] 3 4
append
向切片尾部追加數據,返回新的切片對象; 數據被追加到原底層數組,如果超出cap限制,則為新切片對象重新分配數組,新分配的數組cap是原數組cap的2倍,比如:
{ s := make([]int,0,5) s = append(s , 1) s = append(s , 2,3,4,5) fmt.Printf("%p, %v, %d\n", s,s, cap(s)) s = append(s , 6) //重新分配內存 fmt.Printf("%p, %v, %d\n", s, s, cap(s)) } 輸出: 0xc420010210, [1 2 3 4 5], 5 0xc4200140a0, [1 2 3 4 5 6], 10
如果是向nil切片追加數據,則會高頻率的重新分配內存和數據復制,比如:
{ var s []int fmt.Printf("%p, %v, %d\n", s,s, cap(s)) for i:= 0; i < 10;i++{ s = append(s, i) fmt.Printf("%p, %v, %d\n", s, s, cap(s)) } }
所以為了避免程序運行中的頻繁的資源開銷,在某些場景下建議預留出足夠多的空間。
copy
兩個slice之間復制數據時,允許指向同一個底層數組,并允許目標區間重疊。最終復制的長度以較短的切片長度(len)為準,比如:
{ s1 := []int{0, 1, 2, 3, 4, 5 ,6} s2 := []int{7, 8 ,9} copy(s1,s2) fmt.Println(s1,len(s1),cap(s1)) s1 = []int{0, 1, 2, 3, 4, 5 ,6} s2 = []int{7, 8 ,9} copy(s2,s1) fmt.Println(s2,len(s2),cap(s2)) }
那么可不可以在同一切片之間復制數據呢?
在項目開發過程中,如果slice長時間引用一個大數組中很小的片段,那么建議新建一個獨立的切片,并復制出所需的數據,以便原數組內存可以被gc及時釋優化回收。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。