切片(slice)
切片是Go语言对动态数组的一种实现,切片可以动态扩容,比数组具有更高的灵活性
下文以slice代称
1. 声明
slice的声明有两种方式
- 字面量声明
arr = []int{} - make函数声明
arr := make([]int, 0)
package main
import "fmt"
func main() {
arr0 := []int{}
arr1 := make([]int, 1) //创建了一个长度为1, 容量为1的slice
arr2 := make([]int, 1, 2) //创建了一个长度为1, 容量为2的slice
fmt.Println(len(arr0), cap(arr0)) // 0 0
fmt.Println(len(arr1), cap(arr1)) // 1 1
fmt.Println(len(arr2), cap(arr2)) // 1 2
}
此处的一些细节
- make函数创建slice时,必须指明长度,
make([]int, 0) == []int{} - 容量是可选参数,不指明时
len(arr) == cap(arr) - 长度和容量的区别
长度是指当前slice实际可以存储的元素个数,容量是指底层存储空间的最大元素个数
当长度到达容量时,底层会触发扩容机制,自动分配一个新的底层数组,并将旧数据复制到新的数组中
2. 基本操作
- 追加元素
使用append()函数追加
arr = append(arr,1,2,3)
也可以追加一个切片arr = append(arr, arr1...)
- 修改元素
通过下标(从0开始)进行修改即可,
arr[1] = 2
- 切片
arr[ start : end : max ]
start:切片的起始索引(包含),省略则从0开始
end:切片的结束索引(不包含),省略则到len(arr)结束
max:新的切片的容量上限,表示从start到max的最大索引范围(不包含max本身)。
nums := []int{1, 2, 3, 4, 5}
sub := nums[1:3:4] // 从索引 1 到 3 的切片,最大容量为索引 4
fmt.Println(sub) // 输出:[2, 3]
fmt.Println(len(sub)) // 输出:2,长度为 3-1
fmt.Println(cap(sub)) // 输出:3,容量为 4-1
max参数限制其最大容量,控制切片的增长,防止对原始切片底层数组的某些意外修改。
Go语言的切片是O(1)的(速度很快),因为底层是直接改变指针,而不是复制一个处理,举个例子
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4, 5}
a := nums[:3] // [1,2,3]
//a[0] = 0
//fmt.Println(nums) // [0 2 3 4 5]
a = append(a, 100)
fmt.Printf("%v, %p\n", a, a) //[1 2 3 100], 0xc00000e3c0
fmt.Printf("%v, %p\n", nums, nums) //[1 2 3 100 5], 0xc00000e3c0
nums = []int{1, 2, 3, 4, 5}
b := nums[:3:3] //加了max限制
b = append(b, 200) //此时扩容就是新切片,而不是nums的引用
fmt.Printf("%v, %p\n", b, b) //[1 2 3 200], 0xc0000a8090
fmt.Printf("%v, %p\n", nums, nums) //[1 2 3 4 5], 0xc0000a8060
}
简单来说,切片不是副本,他们会共享相同的底层数组,我们改变切片后会导致原slice发生变化
使用max参数时,可以解决追加元素导致的问题,但不能解决修改元素的问题
想复制一个切片需要使用copy函数
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4, 5}
a := make([]int, 2)
copy(a, nums) //保证len(a) > 0 会复制min(len(a), len(nums))个元素到a中
fmt.Println(a) // [1,2]
a[0] = 0
fmt.Println(a) // [0,2]
fmt.Println(nums) // [1,2,3,4,5]
}
有一些连续操作:比如a := nums[:3][:1]
- 遍历
我们可以使用for i, v := range nums来遍历一个slice,修改v不会对slice造成变化,修改nums[i]会产生变化
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4, 5}
for _, v := range nums {
v = 0 // v只是值拷贝
fmt.Println(v)
}
fmt.Println(nums) //[1 2 3 4 5]
for i := range nums {
nums[i] = 0
}
fmt.Println(nums) //[0 0 0 0 0]
}