1.切片的声明
var s1 []elementType
a."[]"内部没有任何符号,也没有空格
b."[]"后面紧跟的是切片元素的类型
c.切片中的元素类型一致
2.控切片
如果一个切片声明却没有初始化,那么这个切片叫做空切片。空切片没有被分配内存空间,所以不能对空切片赋值
package main
import "fmt"
func main() {
var s1 []int
fmt.Println(s1) //s1 是空切片,打印结果是“[]”
s1[0] = 1
fmt.Println(s1) //内存没有给切片分配空间,所以不能对某个位置赋值,会报错
}
3.切片和 数组的区别
类型 | 数组 | 切片 |
格式声明 | var arr [3]int 或 var arr [...]int | var slice []int |
本质 | 数组是一系列已编号,长度固定的同一类型的数据项序列 | 切片通过内部指针和 相关属性引用的数组片段,长度可变 |
同类型直接的比较 | 不同的数组之间可以相互比较(== 或 !=)。 | 切片之间不能相互比较,切片只能与nil进行比较 |
4.切片和底层数组的关联
a.切片是引用类型,总是指向一个底层数组。
b.切片的数据结果可以抽象为一下3个变量。
一个指针:指向底层数组
长度:接切片中的元素个数
容量:即最大长度,切片已分配的存储空间。
5.切片的创建
5.1直接创建切片
创建方式 | 示例 | 说明 |
先声明后初始化 | var slice1 []int slice1 = []int{1,2,3} | 长度为3,容量为3的切片: slice1 = [1 2 3] |
声明的同时且初始化 | var slice2 []int = []int{1,2,3} | 长度为3,容量为3的切片: slice2 = [1 2 3] |
根据值自行推导类型 | var slice3 = []int{1,2,3} | 长度为3,容量为3的切片: slice3 = [1 2 3] |
赋值操作符创建 | slice4 := []int{1,2,3} | 长度为3,容量为3的切片: slice1 = [1 2 3] |
make()函数创建 | slice5 :=make([]int,0) | 长度为0,容量为0的切片: slice1 = [] //非空切片 |
slice6 :=make([]int,5) | 长度为5,容量为5的切片: slice1 = [0 0 0 0 0] | |
slice7 :=make([]int,0,10) | 长度为0,容量为10的切片: slice1 = [] //非空切片 | |
slice8 :=make([]int,5,10) | 长度为5,容量为10的切片: slice1 = [0 0 0 0 0] |
5.2基于数组创建新切片
类型 | 描述 |
s1 := arr[ : ] | 创建新的切片s1,抽象描述整个数组arr的元素 len(s1) = len(arr) |
s2 := arr[startIndex:endIndex] | 创建新的切片s2,抽象描述数组arr中的strrtIndex到endIndex-1的元素 len(s2) = endIndex - startIndex |
s3 := arr[startIndex:] | 创建新的切片s3,抽象描述数组arr中的strrtIndex到最后一个元素 len(s3) = len(arr) - startIndex |
s4 := arr[:endIndex] | 创建新的切片s4,抽象描述数组arr中的第一个元素到endIndex-1的元素 len(s4) = endIndex |
5.3基于已知切片创建新切片
5.3.1 新切片和已知切片、底层数组之间的关系
新切片基于已知切片的部分或全部元素创建,但新建切片也指向底层数组,新切片中指针存储的为新切片第一个元素在底层数组中存储的空间位置。
5.3.2基于已知切片创建新切片的方式
操作 | 含义 |
slice_b := slice_a[:] | 从切片slice_a截取索引位置0到len(slice_a)-1的元素创建新切片: len(slice_b) = len(slice_a) |
slice_c := slice_a[low:] | 从切片slice_a截取索引位置low到len(slice_a)-1的元素创建新切片: len(slice_c) = len(slice_a) - low |
slice_d := slice_a[:high] | 从切片slice_a截取索引位置0到high-1的元素创建新切片: len(slice_d) = high |
slice_e := slice_a[low:high] | 从切片slice_a截取索引位置low到high-1的元素创建新切片: len(slice_e) = high-low |
slice_f := slice_a[low:high:max] | 从切片slice_a截取索引位置low到high-1的元素创建新切片: len(slice_f) = high-low |
6.切片的操作
6.1对切片的遍历
for i, v := range s{
//s为切片变量名,i为索引值,v为切片元素,满足:v = s[i]
fmt.Printf("str[%d] = %v\t", i, v)
}
6.2在切片中追加元素---append函数
slice1 := append(slice,1,2) //当slice元素类型为整形,追加元素为整形
slice2 := append(slice,"1","2") //当slice元素为字符串型,追加元素为字符串型
slice3 := append(slice,slice_1...)//将slice_1中的所有元素追加到slice3的尾部
import "fmt"
func main() {
s := []int{1, 2, 3}
for i, v := range s {
fmt.Printf("s[%d] = %d\n", i, v)
}
slice1 := append(s, 1, 2)
fmt.Println(slice1)
slice2 := []int{3, 4}
slice3 := append(s, slice2...)
fmt.Println(slice3)
}
6.3切片的拷贝——copy函数
copy(s1,s2)
a.若len(s1) >= len(s2),则切片s2中的元素全部覆盖到切片s1中前m个元素(m=len(s2)),切片s1剩余的元素依然保留不变;
b.如len(s1)<len(s2),则切片s2中只有前n个元素(n=len(s1))能全部覆盖到切片s1中。
6.4 在切片中插入元素
已知切片s,在切片中索引值为index处插入元素,a,b,c。
s2 := append([]int{},s[index:]...)
s1 := append(s[:index])
s1= append(s1, a,b,c)
s = append(s1, s2...)
1,先创建一个s2来保存s从index到最后一个元素
2,创建s1保存s的第一个到index-1的元素
3,在s1后追加a,b,c
4,最后把s2追加到s1后面,然后把产生的新切片,,赋值给s1,完成插入。
切片的拷贝:
package main
import "fmt"
func main() {
s1 := []int{1, 2}
s2 := []int{3}
copy(s1, s2) //len(s1)>len(s2),只覆盖s1前1个元素,余下元素保持不变
fmt.Printf("s1=%v\n", s1)
s3 := []int{3}
s4 := []int{1, 2}
copy(s3, s4) //len(s3)<len(s4),s4中只有一个元素能覆盖s3
fmt.Printf("s3=%v\n", s3)
}
在切片中插入元素:
package main
import "fmt"
func main() {
s := []int{1, 2, 6, 7, 8}
index := 2
/*先将切片在该位置后面的元素都添加到一个空切片,形成一个新切片*/
s2 := append([]int{}, s[index:]...)
/*将包含这个位置及该位置之前的所有元素截取,形成一个新切片s1*/
s1 := append(s[:index])
s1 = append(s1, 3, 4, 5) //在s1后面添加想要插入的元素
/*将s2中的所有元素都添加到s1中,形成的切片s就包含想要插入的元素*/
s = append(s1, s2...)
fmt.Printf("s=%v\n", s)
}
6.5删除切片中的元素
s1 := s[:index]
s2:=s[index+1:]
s=append(s1,s2...)
package main
import "fmt"
func main() {
s := []int{1, 2, 3, 3, 4, 5}
index := 3 //选择要删除的元素的位置
s1 := s[:index] //将该位置之前的所有元素截取为s1
s2 := s[index+1:] //将该位置之后的所有元素截取为s2
s = append(s1, s2...) //将s2中的所有元素添加到s1中
fmt.Printf("s=%v\n", s)
}
6.6切片作为函数参数
a.切片本身的数据结构存在以指向底层数组的指针,切片作为函数参数,其传递方式是引用传递。
b.在自定义函数中对切片进行修改,对应的切片元素也会进行修改。
package main
import "fmt"
//切片做函数参数
func test(s []int) {
s[0] = -1
fmt.Println("test: ")
for i, v := range s {
fmt.Printf("s[%d]=%d\t", i, v)
}
fmt.Println()
}
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
test(slice)
fmt.Println("main: ")
for i, v := range slice {
fmt.Printf("slice[%d]=%d\t", i, v)
}
fmt.Println()
}
注意:实际使用中由于数组长度是固定的,不够灵活,切片用的反而比较频繁。可以把切片看做是动态可变长度的数组。
但是,切片的插入,删除需要频繁重新分配内存,在性能上可能会有影响。
7.切片append的小发现
当append的元素数量和原切片中的总数小于等于原切片的总容量cap(),不会重新分配内存。
当append的元素数量和原切片中的总数大于原切片的总容量cap(),小于2倍的容量cap(),重新分配内存产生新的切片,切片的容量变为原来的2倍。
当append的元素数量和原切片中的总数大于原切片的总容量cap(),大于2倍的容量cap(),元素为奇数时,新的切片cap()为元素总数+1,总数为偶数时,cap()就是元素总数。
即自动扩容的新切片的容量总为偶,
扩容前cap()< 扩容后len(slice) < 2*扩容前cap():新切片容量 = 2*扩容前cap()
扩容后len(slice) > 2*扩容前cap() :扩容后len()为奇数,扩容后容量 = 扩容后len()+1
扩容后len(slice) > 2*扩容前cap() :扩容后len()为偶数,扩容后容量 = 扩容后len()