1、什么是切片
在学习切片前,我们存储相同类型的数据集合只能用数组。但是数组有一个弊端,就是长度固定,无法扩容。这时候,切片就出现了。切片(slice)是对数组的一个连续片段的引用,所以切片是一种引用类型。切片基于数组做了封装,长度可变,支持动态扩容。切片的底层数据结构包含3个元素:指针
、长度
、容量
2、切片的声明初始化
下面代码是切片声明和简单初始化的方法,但是实际开发中很少这样做,一般使用make
关键字来创建切片。
// 声明切片的基本语法
var 变量名 []元素类型
func main() {
var a []string //声明一个字符串型的切片
var b = []int{} //声明一个int型的切片并初始化
var c = []int{1, 2} //声明一个int型的切片并初始化
fmt.Println(a) // []
fmt.Println(b) // []
fmt.Println(c) // [1 2]
}
3、切片表达式
切片是对数组的一个连续片段的引用,golang中提供了切片表达式,我们可以用切片表达式从已有数组中获得切片。
切片表达式基本语法:
数组名[low:high:max]
- low:表示所要切片的对象左索引(从切片对象的哪个地方开始切,包含low索引)
- high:表示所要切片的对象右索引(切片要切到哪里,不包含high索引)
- max:设置切片容量的相关参数,最后切片的容量为
max -low
。max是一个可选参数,如果不设置max,则可以认为max的默认值为数组的长度
简单示例
func main() {
a := [5]int{1,2,3,4,5}
s := a[1:4:4]
// 左包含,右不包含。实际切的是底层数组的索引1,2,3
fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s)) // s:[2 3 4] len(s):3 cap(s):3
}
需要注意的是数组越界的问题:参数要满足0 <= low <= high <= max <= len()
4、使用make创建切片
使用make
关键字创建切片是最常用的方式。
基本语法:
slice := make([]type, len, cap)
- type:元素类型
- len:切片的长度(实际元素数量)
- cap:切片的容量
使用make创建切片时,长度和容量是必要的参数。如果两个参数都填的话,则长度和容量都按相应的参数设置。如果只填一个参数的话,则长度和容量的值都为该参数。切片中元素的默认值则为对应类型的零值。
简单示例
func main() {
s1 := make([]int, 2, 3)
// 切片数据:[0 0],切片长度:2,切片容量:3
fmt.Printf("切片数据:%v,切片长度:%v,切片容量:%v \n", s1, len(s1), cap(s1))
s2 := make([]int, 3)
// 切片数据:[0 0 0],切片长度:3,切片容量:3
fmt.Printf("切片数据:%v,切片长度:%v,切片容量:%v \n", s2, len(s2), cap(s2))
}
5、切片的底层数据结构
我们知道切片是对数组的一个连续片段的引用。它包含了3个元素:指向底层数组片段的指针,切片长度(切片中元素的数量),切片容量(切片指向的片段所能容纳元素数量)
简单示例
现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
,切片s1 := a[:5]
,相应示意图如下:
切片s2 := a[3:6]
,相应示意图如下:
6、切片相关操作方法
6.1 切片的拷贝
切片是一种引用类型,所以切片的拷贝分为深拷贝和浅拷贝。深拷贝指的是将切片的数据复制到另一块内存中,赋值给新变量,两个变量对切片的操作。浅拷贝指的是将切片的引用地址赋值给新变量。
深拷贝
深拷贝指的是将切片的数据复制到另一块内存中。这样,两个切片的操作互不影响。
func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := make([]int, 5, 5)
// 复制切片s1的底层数组给s2
copy(s2, s1)
fmt.Println(s1) // [1 2 3 4 5]
fmt.Println(s2) // [1 2 3 4 5]
s2[1] = 10
fmt.Println(s1) // [1 2 3 4 5]
fmt.Println(s2) // [1 10 3 4 5]
}
浅拷贝
浅拷贝指的是将切片变量赋值给另一变量,两个变量共享切片底层数组,操作切片的话会互相影响。
func main() {
a := [5]int{1, 2, 3, 4, 5}
s1 := a[1:4]
s2 := s1
fmt.Println(s1) // [2 3 4]
fmt.Println(s2) // [2 3 4]
s2[1] = 10
fmt.Println(s1) // [2 10 4]
fmt.Println(s2) // [2 10 4]
}
6.2 切片遍历
切片的遍历和数组一样,支持索引遍历和for range
遍历。
func main() {
s := []int{1, 3, 5}
// 索引遍历
for i := 0; i < len(s); i++ {
fmt.Println(i, s[i])
}
// for range遍历
for index, value := range s {
fmt.Println(index, value)
}
}
6.3 往切片中添加元素
golang中内置函数append()
可以动态地往切片中添加元素,支持添加一个或多个元素,同时支持动态添加切片。
func main() {
var s []int
s = append(s, 1)
fmt.Println(s) // [1]
s = append(s, 2, 3) // [1 2 3]
fmt.Println(s)
s1 := make([]int, 2, 2)
s1[0] = 4
s1[1] = 5
s = append(s, s1...)
fmt.Println(s) // [1 2 3 4 5]
}
6.4 从切片中删除元素
func main() {
s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
// 删除元素5
s = append(s[:4], s[5:]...)
for _, value := range s {
fmt.Println(value)
}
}