切片概述
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比,切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
切片是可索引的,并且可以由 len() 方法获取长度。
切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
定义切片
package main
import "fmt"
func main() {
//1. 声明一个切片,此时为nil
var s1 []int
fmt.Println(s1) //[]
fmt.Println(s1 == nil) //true
fmt.Printf("%s的长度为%d,容量为%d\n", "s1", len(s1), cap(s1)) //s1的长度为0,容量为0
//2. 使用make函数创建一个int类型,长度为2的切片
s2 := make([]int, 2) //make(类型,长度)
fmt.Println(s2) //[0 0]
fmt.Println(s2 == nil) //false
fmt.Printf("%s的长度为%d,容量为%d\n", "s2", len(s2), cap(s2)) //s2的长度为2,容量为2
//3. 声明一个切片,并初始化
s3 := []int{1, 3, 5}
fmt.Println(s3) //[1 3 5]
fmt.Println(s3 == nil) //false
fmt.Printf("%s的长度为%d,容量为%d\n", "s3", len(s3), cap(s3)) //s3的长度为3,容量为3
//4. 使用make函数创建一个int类型,长度为4,容量为5
s4 := make([]int, 4, 5) //make(类型,长度,容量)
fmt.Println(s4) //[0 0 0 0]
fmt.Println(s4 == nil) //false
fmt.Printf("%s的长度为%d,容量为%d\n", "s4", len(s4), cap(s4)) //s4的长度为4,容量为5
}
创建切片
package main
import "fmt"
func main() {
//1. make函数创建切片
s1 := make([]int, 3, 10)
fmt.Println(s1) //[0 0 0]
//1. 从数组创建切片
arr := [5]int{1, 3, 5, 7, 9}
s2 := arr[:]
s3 := arr[1:]
s4 := arr[:3]
s5 := arr[1:3]
fmt.Println(s2) //[1 3 5 7 9]
fmt.Println(s3) //[3 5 7 9]
fmt.Println(s4) //[1 3 5]
fmt.Println(s5) //[3 5]
//3. 从切片创建切片
s6 := s5[:]
fmt.Println(s6) //[3 5]
}
append()函数
package main
import "fmt"
func main() {
s1 := []string{"1", "2", "3", "4"}
fmt.Printf("len(s1)=%d cap(s1)=%d\n", len(s1), cap(s1)) //len(s1)=4 cap(s1)=4
//1. 追加一个元素
s1 = append(s1, "5")
fmt.Printf("len(s1)=%d cap(s1)=%d\n", len(s1), cap(s1)) //len(s1)=5 cap(s1)=8
//2. 追加多个元素
s1 = append(s1, "6", "7", "8")
fmt.Printf("len(s1)=%d cap(s1)=%d\n", len(s1), cap(s1)) //len(s1)=8 cap(s1)=8
//3. 追加一个切片
s2 := []string{"1", "2", "3"}
s1 = append(s1, s2...)
fmt.Printf("len(s1)=%d cap(s1)=%d\n", len(s1), cap(s1)) //len(s1)=11 cap(s1)=16
}
append()追加元素,原来的底层数组容量不够时,go底层会把底层数组替换,是go语言的一套扩容策略。
数组转切片
package main
import "fmt"
func main() {
a1 := [...]int{1, 2, 3}
s1 := a1[:]
fmt.Printf("a1类型:%T\n", a1) //a1类型:[3]int
fmt.Printf("s1类型:%T\n", s1) //s1类型:[]int
}
切片的本质
- 切片的本质:切片的本质是一个框,框住了一块连续的内存;
- 切片属于引用类型,真正的数据都是保存在底层数组里的;
- 切片可以简单理解为是快捷方式,修改会互相影响;
- 判断一个切片是否为空,使用len(s) == 0 判断,不能使用 s==nil 判断;
切片的实现
slice 底层的数据结构是一个结构体,包括三个元素:
- array(Pointer) 是指向一个数组的指针
- len 是当前 slice 的长度
- cap 是当前 slice 的容量
切片的扩容
slice 的扩容是一个很智能的动作,当发生扩容时,slice 会有两个动作:
- 扩大 slice 内部的 cap 值(按照 2 倍或者 1.25 倍扩大)。
- 重新申请一个 array,把原数据 copy 到这个新 array 上,然后把 slice 的 array 指针指向这个申请的 array。