因为数组的长度是固定、传值机制会导致较大的内存拷贝开销,因此在 Go 语言中很少直接使用数组。
切片是一个拥有相同类型元素的可变长度的序列,它是基于数组类型做的一层封装,功能更灵活,支持自动扩容和收缩。
每个切片包含三个字段:
- array: 是指向底层数组的指针;
- len:是切片的长度,即切片中当前元素的个数;
- cap:是底层数组的长度,也是切片的最大容量,cap值永远大于等于len值。
Go编译器会自动为每个新创建的切片建立一个底层数组,默认底层数组的长度与切片初始元素个数相同。
创建切片
s := make([]string, 6, 10) // 10位cap的值,即底层数组长度,6为切片的初始长度
// 如果没有再make中指定cap参数,那么底层数组长度cap就等于len
s := make([]string, 6) // cap = len = 6
s := []string{}
s := []string{"a", "b", "c"}
s := []string{1: "a", 2: "b", 3: "c"}
添加元素
s := make([]string, 6, 10)
s = append(s, "a", "b")
fmt.Printf("%#v\n", s) // []string{"", "", "", "", "", "", "a", "b"}
获取切片长度和容量
fmt.Println(len(s), cap(s))
遍历切片
// for
for i := 0; i < len(s); i++ {
fmt.Println(i, s[i])
}
// range
for i, v := range s {
fmt.Println(i, v)
}
切片删除
Go没有直接的切片删除功能
package main
import "fmt"
func main() {
s := []int{1, 2, 3, 4, 5}
// 移除第一个元素
s = s[1:]
// 移除最后一个元素
s = s[:len(s)-1]
fmt.Println(s) // [2 3 4]
}
切片的动态扩容
“动态扩容”指的就是,当我们通过append操作向切片追加数据的时候,如果这时切片的len值和cap值是相等的,也就是说切片底层数组已经没有空闲空间再来存储追加的值了,Go运行时就会对这个切片做扩容操作,来保证切片始终能存储下追加的新值。
当切片的len值和cap值相等的情况下继续向切片追加数据时,append会判断出底层数组剩余空间不够,就会创建一个新的底层数组,这个新的底层数组的长度等于之前底层数组长度的2倍,也就是切片的容量,新的底层数组建立后,append会把旧底层数组中的数据拷贝到新数组中,之后新数组便成为了切片的底层数组,旧的底层数组会被垃圾回收掉。