切片
切片並不是數組或者數組指針,切片只是對數組中連續片斷的引用,這個片斷可以是整個數組,也可以是由起始索引和終止索引標識的數組,所以切片是一個引用類型。
// 切片的數據結構
struct Slice{
byte* array;
uintgo len;
uintgo cap;
}
-
切片是引用類型,但是自身是結構體,調用函數時是值傳遞。
-
len 屬性獲取長度,cap 屬性獲取容量
初始化
func main() {
s1 := []int{}
var s2 []int
s3 := make([]int,0)
// s1[0] = 1 以下三種賦值都會報錯
// s2[0] = 1 panic: runtime error: index out of range [0] with length 0
// s3[0] = 1
s1 = append(s1, 1) // Slice 正確添加值的方式
s2 = append(s2, 1)
s3 = append(s3, 1)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
}
一個切片在未初始化之前默認為 nil,長度為 0
或者通過數組創建切片:
func main() {
arrStr := [10]string{"a","b","c","d","e","f","h","i","j","k"}
slice1 := arrStr[:] // 包含數組的全部元素
slice2 := arrStr[3:] // 包含從第三個元素起一直到最后一個元素
slice3 := arrStr[:8] // 包含第8個元素之前的所有元素
fmt.Println(slice1)
fmt.Println(slice2)
fmt.Println(slice3)
// 輸出:
// [a b c d e f h i j k]
// [d e f h i j k]
// [a b c d e f h i]
}
通過這個方式創建數組,實際上三個切片的索引還在底層數組上,只是表現的形式不同
獲取切片的長度和容量:
fmt.Println("slice2 的長度是:",len(slice2))
fmt.Println("slice2 的容量是:",cap(slice2))
// 輸出:
// slice2 的長度是: 7
// slice2 的容量是: 7
append
因為切片是一個引用類型,底層是一個數組,所以切片很容易就可以完成動態擴容。
func main() {
slice := make([]int,0)
for i := 0; i < 10; i++ {
slice = append(slice, i)
fmt.Printf("長度:%d,容量:%d\n", len(slice), cap(slice))
}
// 輸出:
// 長度:1,容量:1
// 長度:2,容量:2
// 長度:3,容量:4
// 長度:4,容量:4
// 長度:5,容量:8
// 長度:6,容量:8
// 長度:7,容量:8
// 長度:8,容量:8
// 長度:9,容量:16
// 長度:10,容量:16
}
切片后添加值使用 append 方法,直接在切片尾部添加值,並返回一個新的切片,之前的切片如果沒有使用,就會被GC回收。
根據上面的代碼,可以得出:切片在每次長度=容量之后,切片的容量就會擴大兩倍
func main() {
slice := make([]int,0)
for i := 0; i < 1030; i++ {
slice = append(slice, i)
if i > 1022{
fmt.Printf("長度:%d,容量:%d\n", len(slice), cap(slice))
}
}
// 輸出:
// 長度:1024,容量:1024
// 長度:1025,容量:1280
// 長度:1026,容量:1280
// 長度:1027,容量:1280
}
根據這一次的代碼,可以得出:切片的容量超過 1024 (1M)之后,每一次擴容都會增加前一次容量的 1/4
- 切片的容量在 1024 之下時,每一次擴容容量翻倍
- 切片的容量在 1024 之上時,每一次擴容容量增加前一次容量的 1/4
關注公眾號,隨時獲取最新資訊
細節決定成敗!
個人愚見,如有不對,懇請斧正!