一、概念
Go 语言的切片是一种引用类型,它是数组的一个连续片段的引用。
那么为什么已经有了数组还需要切片呢?
首先,数组是固定长度的而切片是可变长度的,更灵活。
其次,拷贝数组的效率比切片高。
所以在日常开发中,使用切片的频率要远高于数组。
三、创建切片
直接申明
var s []int
fmt.Println(s == nil) // true
字面量初始化
s := []int{1, 2, 3}
fmt.Println(len(s), cap(s)) // 3 3
从数组引用
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4]
fmt.Println(s)
make 创建
s := make([]int, 3, 5)
fmt.Println(len(s), cap(s)) // 3 5
底层会分配一个长度为 cap 的数组,并返回前 len 个元素的切片
二、切片的常用操作
遍历切片
var s = []int{1, 2, 3}
for i, e := range s {
fmt.Println(i, e)
}
追加切片
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。下面的代码描述了从拷贝切片的 copy 函数和向切片追加新元素的 append() 函数
package main
import "fmt"
func main() {
slFrom := []int{1, 2, 3}
slTo := make([]int, 10)
n := copy(slTo, slFrom)
fmt.Println(slTo)
fmt.Printf("Copied %d elements\n", n) // n == 3
sl3 := []int{1, 2, 3}
sl3 = append(sl3, 4, 5, 6)
fmt.Println(sl3)
}
清空切片
s = s[:0] // 清空内容,但不释放底层数组
// 或者
s = nil // 同时释放底层数组
四、切片易错知识点
索引越界
跟 Python 等语言一样,获取不存在的索引项,会出错
s := []int{1, 2, 3}
_ = s[3] // panic: index out of range
nil 与空切片区别
var a []int // nil
b := []int{} // 空切片
fmt.Println(a == nil) // true
fmt.Println(b == nil) // false
切片共享机制!
arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[1:4]
s2 := s1[1:3]
s2[0] = 99
fmt.Println(arr) // [1 2 99 4 5]
修改 s2 的内容会影响 s1 和 arr
append 函数重分配
append 触发重新分配,导致数据不再共享
s1 := []int{1,2,3}
s2 := s1
s2 = append(s2, 4)
fmt.Println(s1) // [1 2 3]
fmt.Println(s2) // [1 2 3 4]