slice简介
slice
表示一个拥有相同类型元素的可变长度的序列(动态数组),我们也叫做切片。
slice
有三个属性:指针、长度和容量。
- 指针指向数组的第一个可以从
slice
访问的元素,这个元素不一定是数组的第一个元素。 - 长度指
slice
的元素个数 - 容量的大小是从
slice
的起始元素到底层数组的最后一个元素间元素的个数。
slice的初始化
slice := make([]int, 5) // 内置make函数
slice := make([]int, 5, 10) // 创建长度为5,容量为10的切片
slice := []int{1,2,3,4,5} // 数组形式初始化
slice := []int{4:1} // 指在定了第5个元素为1,其他默认为0
切片的引用
一个底层数组可以对应多个slice
,这些slice
可以引用数组的任何位置
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := s1[1:4]
fmt.Println(s1) // [1 2 3 4 5]
fmt.Println(s2) // [2 3 4]
}
slice
操作符s[i:j]
创建了一个新的slice
,这个新的slice
引用了序列s
中从[i, j)
的所有元素,是左闭右开的形式。
所以s2 := s1[1:4]
相当于s2
引用了s1[1, 4)
索引的元素,索引从0开始。
我们还可以查看s2的容量和长度
fmt.Printf("s1.len = %d, s1.cap = %d\n", len(s1), cap(s1)) // s1.len = 5, s1.cap = 5
fmt.Printf("s2.len = %d, s2.cap = %d\n", len(s2), cap(s2)) // s2.len = 3, s2.cap = 4
s2
引用了s1[1, 4)
的元素,所以元素数量为3,而容量的大小是从slice
的起始元素到底层数组的最后一个元素间元素的个数,s2
以数组的第二个索引元素作为切片的开头,所以其容量小1
slice无法作比较
与数组不同的是,slice
无法作比较,不能使用==
。slice
的元素不是直接的,其引用的底层数组可能会发生改变,同一个slice
在不同的时间可能会有不同的值。
append
既然是动态数组,那么也可以添加元素
s := []int{1, 2, 3}
fmt.Println(s, len(s), cap(s)) // [1 2 3] 3 3
s = append(s, 10)
fmt.Println(s, len(s), cap(s)) // [1 2 3 10] 4 6
可以看到,我们需要使用原来的切片来接收append
传送回来的新切片。可以看到,我们底层数组的容量发生了改变,其实就是超出了容量需要重新找寻另一个空间,并复制元素到新的空间。和vector
的增长原理类似,扩增一倍。
一个演示容量扩增的实例
func expend() {
var y []int
for i := 0; i < 10; i++ {
y = append(y, i)
fmt.Printf("%d cap = %d\t%v\n", i, cap(y), y)
}
}
0 cap = 1 [0]
1 cap = 2 [0 1]
2 cap = 4 [0 1 2]
3 cap = 4 [0 1 2 3]
4 cap = 8 [0 1 2 3 4]
5 cap = 8 [0 1 2 3 4 5]
6 cap = 8 [0 1 2 3 4 5 6]
7 cap = 8 [0 1 2 3 4 5 6 7]
8 cap = 16 [0 1 2 3 4 5 6 7 8]
9 cap = 16 [0 1 2 3 4 5 6 7 8 9]