跳到主要内容

切片

切片声明时默认的长度和容量

切片在使用 make 声明时,如果没有显式的说明切片的容量,那么默认容量和切片的长度保持一致。

func main() {
s1 := make([]int, 3)
fmt.Println("s1 length: ", len(s1)) // s1 length: 3
fmt.Println("s1 capacity: ", cap(s1)) // s1 capacity: 3
fmt.Printf("s1 value: %#v\n", s1) // s1 value: []int{0, 0, 0}
}

切片声明时指定容量

func main() {
s2 := make([]int, 3, 7)
fmt.Println("s2 length: ", len(s2)) // s2 length: 3
fmt.Println("s2 capacity: ", cap(s2)) // s2 capacity: 7
fmt.Printf("s2 value: %#v\n", s2) // s2 value: []int{0, 0, 0}
}

指定切片区间作为新切片时,新切片的长度和容量

func main() {
s3 := []int{1, 2, 3, 4, 5, 6, 7, 8}
s4 := s3[3:6]
fmt.Printf("s4 length is: %d\n", len(s4)) // s4 length is: 3
fmt.Printf("s4 capacity is: %d\n", cap(s4)) // s4 capacity is: 5
fmt.Printf("s4 value is: %d\n", s4) // s4 value is: [4 5 6]

s5 := s4[:cap(s4)]
fmt.Printf("s5 length is: %d\n", len(s5)) // s5 length is: 5
fmt.Printf("s5 capacity is: %d\n", cap(s5)) // s5 capacity is: 5
fmt.Printf("s5 value is: %d\n", s5) // s5 value is: [4 5 6 7 8]
}

切片 s4 是在切片 s3 的基础上通过索引划分出来一个新的切片,所以切片 s4 的长度是 6-3=3,而切片 s4 的容量 cap(s4)=8-3=5,其中 8 为切片 s3 的长度,3 为切片s4 在s3 的起始位置。

cap(s4)=5,所以 s4[:cap(s4)] 就等于 s4[:5] 由于 s4s3 共用同一个数组,所以s4[:5] 就相当于从 s4 起始的位置开始到第 5 个元素,也就是从 s3 的第三个元素开始一直到第 3 + 5 = 8 个元素,所以 s5=[4 5 6 7 8]

切片扩容时容量的变化

切片通过 append 扩容时,如果切片长度小于当前的容量,那么切片不会扩容,如果追加元素后切片长度大于当前的容量时,切片就会扩容,扩容机制如下:

  • 当扩容之后的元素长度小于 1024 时会以原切片容量的 2 倍的进行扩容;

  • 当扩容之后的元素长度大于 1024 时会以原切片容量的 1.25 倍的进行扩容;

  • 当不需要扩容时,append 函数返回的是原底层数组的原切片(内存地址不变)

  • 当切片需要扩容时,append 函数返回的是新底层数组的新切片(切片内存地址发生了改变);

向 slice 追加元素的时候,若容量不够,会调用 growslice 函数

func growslice(et *_type, old slice, cap int) slice {
// ……
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
for newcap < cap {
newcap += newcap / 4
}
}
}
// ……
capmem = roundupsize(uintptr(newcap) * ptrSize)
newcap = int(capmem / ptrSize)

多个切片指向同一个数组,对切片元素的修改会影响到整个数组

如果多个切片指向同一底层数组,引用相同片段的底层数组会因为其中一个切片的改变影响整个底层数组, 因此需要特别注意。

func main() {
a1 := [7]int{1, 2, 3, 4, 5, 6, 7}
fmt.Printf("a1: %v (len: %d, cap: %d)\n", a1, len(a1), cap(a1))
s9 := a1[1:4]

fmt.Printf("s9: %v (len: %d, cap: %d)\n", s9, len(s9), cap(s9))
for i := 1; i <= 5; i++ {
s9 = append(s9, i)
fmt.Printf("s9(%d): %v (len: %d, cap: %d)\n", i, s9, len(s9), cap(s9))
}
fmt.Printf("a1: %v (len: %d, cap: %d)\n", a1, len(a1), cap(a1))
}