1.数组与切片的区别与联系
- 数组的值的长度是固定的,而切片的值是可变长的
- 切片的长度可以自动地随着其中元素数量的增长而增长,但不会随着元 素数量的减少而减少
- 切片是引用类型,数组是值类型
- 切片的容量表示其底层数组的长度
- 切片的长度表示可以连续获取其底层数组中元素的数量
2.怎样估算切片容量的增长?
- 当一个切片无法容纳更多的元素时就会扩容,但是并不会改变原来的切片,而是会生成一个容量更大的切片,然后将原有的元素和新增的元素拷贝至新切片
- 一般情况下新切片的容量是原切片容量的2倍
- 当原切片的长度(以下简称原长度)大于或等于1024时,Go 语言将会以原容量的1.25 倍作为新容量的基准,新容量基准会被调整(不断地与1.25相乘),直到结果不小于原长度与要追加的元素数量之和
- 如果一次追加的元素过多,以至于使新长度比原容量的 2 倍还要大,那么新容量就 会以新长度为基准
package main
import "fmt"
func main() {
s6 := make([]int, 0)
fmt.Printf("The capacity of s6: %d\n", cap(s6))
for i := 1; i <= 5; i++ {
s6 = append(s6, i)
fmt.Printf("s6(%d): len: %d, cap: %d\n", i, len(s6), cap(s6))
}
fmt.Println("-----------------------------------------------------------------")
s7 := make([]int, 1024)
fmt.Printf("The capacity of s7: %d\n", cap(s7))
s7e1 := append(s7, make([]int, 200)...)
fmt.Printf("s7e1: len: %d, cap: %d\n", len(s7e1), cap(s7e1))
s7e2 := append(s7, make([]int, 400)...)
fmt.Printf("s7e2: len: %d, cap: %d\n", len(s7e2), cap(s7e2))
s7e3 := append(s7, make([]int, 600)...)
fmt.Printf("s7e3: len: %d, cap: %d\n", len(s7e3), cap(s7e3))
fmt.Println("-----------------------------------------------------------------")
s8 := make([]int, 10)
fmt.Printf("The capacity of s8: %d\n", cap(s8))
s8a := append(s8, make([]int, 11)...)
fmt.Printf("s8a: len: %d, cap: %d\n", len(s8a), cap(s8a))
s8b := append(s8a, make([]int, 23)...)
fmt.Printf("s8b: len: %d, cap: %d\n", len(s8b), cap(s8b))
s8c := append(s8b, make([]int, 45)...)
fmt.Printf("s8c: len: %d, cap: %d\n", len(s8c), cap(s8c))
}
3.切片的底层数组什么时候会被替换?
- 一个切片的底层数组永远不会被替换。虽然在扩容的时候 Go 语言一定会生成新的底层数组,但是它也同时生成了新的切片。它是把新的切片作为了新底层数组的窗口,而没有对原切片及其底层数组做任何改动
- 在无需扩容时,append函数返回的是指向原底层数组的新切片,而在需要扩容时, append函数返回的是指向新底层数组的新切片
package main
import "fmt"
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]
//s9[0] = 1
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))
fmt.Println()
}
4.思考题
(1)如果有多个切片指向了同一个底层数组,那么你认为应该注意些什么?
答:对切片的修改都会反映在底层数组中
(2)怎样沿用“扩容”的思想对切片进行“缩容”?
答:生成新切片,缩小窗口大小
最后本节涉及代码可以参考:demo GitHub地址