切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
切片是一个引用类型(直接引用的内存地址),它的内部结构包含地址、长度和容量。切片一般用于快速地操作一块数据集合。
切片是引用类型,不支持直接比较,只能和nil比较!
得到切片的三种方式:
- 直接声明
a := []int{1,2,3}
len:3 cap:3 - 基于数组得到切片
m := [5]int b:= m[:3]
len:3, cap:5,数组发生改变,切片随即发生改变,反之亦然, 只取了 数组m 的前三个 - 基于切片得到切片
b := a[:2]
len:2 cap:3; 只取了切片 a的前两个
切片定义
基本语法:var name []T
name:变量名
T:切片中元素类型
切片定义
//基本声明:直接定义
var a []int //声明一个切片
var b = []int{1,2,3} //声明一个切片并且初始化
a = []int{1,3,5}
fmt.Println(a) //[1 3 5]
fmt.Println(b) //[1 2 3]
//从数组中定义
c := [4]int{1,2,3,4}
c1 := c[0:2] //包含元素c[0],c[1],左包含右不包含
fmt.Println(c1) //[1 2]
fmt.Printf("type of c1:%T
", c1) //type of c1:[]int
c2 := c[1:] //从索引1开始切到最后
c3 := c[:3] //从开始切到索引为3(不包含3,3-1)
fmt.Println(c2) //[2 3 4]
fmt.Println(c3) //[1 2 3]
切片扩容
Go语言的内建函数append()可以为切片动态添加元素。
示例:
//切片扩容
s := []int{} //定义一个空切片
//var s []int //定义一个空切片
for i:=0;i<10;i++{
s = append(s, i)
fmt.Printf("%v len:%d cap:%d ptr:%p
",s,len(s),cap(s),s)
}
//输出
[0] len:1 cap:1 ptr:0xc42000e318
[0 1] len:2 cap:2 ptr:0xc42000e340
[0 1 2] len:3 cap:4 ptr:0xc42000a360
[0 1 2 3] len:4 cap:4 ptr:0xc42000a360
[0 1 2 3 4] len:5 cap:8 ptr:0xc4200161c0
[0 1 2 3 4 5] len:6 cap:8 ptr:0xc4200161c0
[0 1 2 3 4 5 6] len:7 cap:8 ptr:0xc4200161c0
[0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc4200161c0
[0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc42007a000
[0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc42007a000
- append()函数将元素追加到切片的最后并返回该切片。
- 切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容。
- 如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)。
- 如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap)
- 如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
- 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。
切片是引用类型
//对一个切片的修改会影响另一个切片的内容
a := []int{1,2,3}
b := a //直接赋值
b[1] = 10
fmt.Println(a) //[1 10 3]
fmt.Println(b) //[1 10 3]
由于切片是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。
make()函数构造切片
如果需要动态的创建一个切片,我们就需要使用内置的make()函数。
语法:make([]T, size, cap)
T:切片的元素类型
size:切片中元素的数量
cap:切片的容量
make用于给引用类型申请内存空间
切片必须初始化,或者用append才能使用
//make函数
s := make([]int,2,5)
fmt.Println(s) //[0 0]
fmt.Println(len(s)) //2
fmt.Println(cap(s)) //5
s的内部存储空间已经分配了5个,但实际上只用了2个.
copy()函数复制切片
Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,修改copy后切片的值不会影响原切片。
语法:copy(destSlice, srcSlice []T)
srcSlice: 数据来源切片
destSlice: 目标切片
a := []string{"北京","上海"}
var b []string //还没有申请内存,所以打印为[]
copy(b,a)
fmt.Println(a) //[北京 上海]
fmt.Println(b) //[]
c := make([]string,3,3)
copy(c,a)
fmt.Println(c) //[北京 上海 ]
c[0] = "青岛"
fmt.Println(c) //[青岛 上海 ]
fmt.Println(a) //[北京 上海]
切片删除元素
Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。
//切片中删除元素
d := []int{1,2,3,4,5,6,7}
//删除索引为2的元素
d = append(d[:2],d[3:]...)
fmt.Println(d) //[1 2 4 5 6 7]
总结:要从切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)
切片遍历
支持索引遍历和for range遍历。
//切片遍历
a1 := []string{"北京","上海"}
for i:=0;i<len(a1);i++{
fmt.Println(a1[i])
}
for k,v :=range a1{
fmt.Println(k,v)
}