数组
创建方式
提起切片,我们必须要先了解数组,数组的类型名是[n]elementType,n代表数组长度,elementType是数组元素类型。数组的定义方式如下:
// var elementName [n]elementType
var array [2]int // [0 0];声明一个含有两个int类型的数组,因为未初始化,所以元素都为默认值0
array2:=[...]int{1,2,3} // 可以不指定数组长度,但必须指定"...",然后编译器自动根据字面量初始化列表推导数组长度。
数组常见创建方式
arr1:=[3]int{1,2,3} // [1 2 3]
arr2:=[...]int{1,2,3} // [1 2 3]
arr3:=[3]int{1:1,2:2} // [0 1 2]
arr4:=[...]int{3:3,5:5} // [0 0 0 3 0 5]
数组特点
长度固定,不可以超过长度追加元素
-
数组是值类型,数组赋值或者作为函数参数都是值拷贝
a:=[2]int{1,2} b:=a b[0]=999 fmt.Println("a:",a) // a: [1 2] fmt.Println("b:",b) // b: [999 2]
数组长度是数组类型的组成部分,[1]int和[2]int表示不同的类型
-
可以根据数组创建切片,切片引用的是原数组,切片的改变会引起原数组的改变
a:=[2]int{1,2} b:=a[:] b[0]=999 fmt.Println("a:",a) // a: [999 2] fmt.Println("b:",b) // b: [999 2]
遍历数组
a:=[2]int{1,2}
// idx是索引,v是值
for idx,v:=range a{
fmt.Println(idx,v)
}
for i:=0;i<len(a);i++{
fmt.Println(i,a[i])
}
切片
因为数组的长度固定,而且是值拷贝,所以限制了数组的使用场景。此时切片(slice)应运而生,它是一种变长数组,它是一种引用类型(map、channel也是引用类型)。
slice的底层如下:
type slice struct {
array unsafe.Pointer // 指向底层数组的指针
len int // 切片的元素数量
cap int // 底层数组容量
}
创建方式
-
由数组创建:array[s:e],array是数组名,s是起始索引,e是终止索引,array[s:e]包括s索引下的数据,但不包括e索引下的数据(相当于左闭右开[s,e)),也就是说第一个元素的索引是s,最后一个元素的索引是e-1,该切片的长度为e-s
var array = [3]int{1,2,3} a1:=array[:] // [1 2 3],如果不指定起始或者终止索引的话,起始索引默认为0,终止索引默认为len(array) a2:=array[1:2] // [2]
同样方式也可以通过切片创造切片
s:=[]int{1,2,3} b:=s[1:2] //[2]
-
由内置函数make创建
通过make创建的切片的元素初始值都被默认初始化为元素类型的零值
arr1:=make([]int,5) // [0 0 0 0 0] len=5,cap=5,所有元素都被默认初始化为0 arr2:=make([]int,5,10) // [0 0 0 0 0] len=5,cap=10,所有元素都被默认初始化为0
-
由字面量初始化
arr:=[]int{1,2,3} // [1 2 3] len=3 cap=3
常见操作
s:=[]int{1,2}
fmt.Println(len(s)) // len=2,返回切片长度
fmt.Println(cap(s)) // cap=2,返回切片容量
s=append(s,3) // 向s追加元素;切片元素:[1,2,3];len=3,cap=4,切片长度超过原来的容量,自动扩容,容量扩大为原来的两倍
s1:=[]int{4,5}
s=append(s,s1...) // 向s追加切片
fmt.Println(s) // [1 2 3 4 5]
s=append(s,7,8) // 向切片追加多个元素
fmt.Println(s) // [1 2 3 4 5 7 8]
c:=[]int{1,2,3}
b:=make([]int,2) // [0 0]
copy(b,c) //只会复制长度较小的,第一个参数是目的切片,第二个参数是源切片
fmt.Println(b) // [1 2]
c1:=[]int{1,2,3}
b1:=make([]int,5) // [0 0 0 0 0]
copy(b1,c1)
fmt.Println(b1) // [1 2 3 0 0]
Tips:
当切片的长度超过容量cap的时候,切片会进行扩容,cap直接扩大为原来的2倍。
-
append函数定义如下,第一个参数表示向哪个切片追加元素,后面的是可变参数,可以添加多个参数,也可以添加一个切片,当第二个参数是切片时,需要在切片后面添加三个点来表示引用切片中所有元素"..."
注意:可变参数可以是单个元素,也可以是一个切片…,但不能两者同时出现。
func append(slice []Type, elems ...Type) []Type
s:=[]int{1,2}
s1:=[]int{4,5}
// s=append(s,1,s1...) // 报错
// s=append(s,s1...,1) // 报错
字符串和切片相互转换
str:="深夜幽魂"
a:=[]byte(str) // [230 183 177 229 164 156 229 185 189 233 173 130]
b:=[]rune(str) // [28145 22812 24189 39746]
c:=[]byte{230,183,177,229,164,156,229,185,189,233,173,130}
fmt.Println(string(c)) // 深夜幽魂
d:=[]rune{28145,22812,24189,39746}
fmt.Println(string(d)) // 深夜幽魂
Tips:字符串转切片的时候需要注意,尤其当字符串内容很大的时候,因为每一次都要复制内容
刁钻难点
- 通过var b []int创建切片,该切片底层array指针不指向任何数组(也就是array的值为nil),len、cap都是0;但通过a:=[]int{}这种方式创建的切片,虽然len、cap也都是0,但是底层array是开辟了空间的,不为nil
a:=[]int{}
var b []int
fmt.Println(a,a==nil,len(a),cap(a)) // [] false 0 0
fmt.Println(b,b==nil,len(b),cap(b)) // [] true 0 0
-
通过array[s:e]创建切片的时候,其实还可以这样array[s:e:c],c的最大值是数组的长度或者切片的容量的最大值。e-s是该切片的长度,c-s是该切片的容量
a:=[]int{1,2} b:=a[0:1:2] fmt.Println(b,len(b),cap(b)) // [1] 1 2