數(shù)組
創(chuàng)建方式
提起切片,我們必須要先了解數(shù)組,數(shù)組的類型名是[n]elementType,n代表數(shù)組長(zhǎng)度,elementType是數(shù)組元素類型。數(shù)組的定義方式如下:
// var elementName [n]elementType
var array [2]int // [0 0];聲明一個(gè)含有兩個(gè)int類型的數(shù)組,因?yàn)槲闯跏蓟?,所以元素都為默認(rèn)值0
array2:=[...]int{1,2,3} // 可以不指定數(shù)組長(zhǎng)度,但必須指定"...",然后編譯器自動(dòng)根據(jù)字面量初始化列表推導(dǎo)數(shù)組長(zhǎng)度。
數(shù)組常見(jiàn)創(chuàng)建方式
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]
數(shù)組特點(diǎn)
長(zhǎng)度固定,不可以超過(guò)長(zhǎng)度追加元素
-
數(shù)組是值類型,數(shù)組賦值或者作為函數(shù)參數(shù)都是值拷貝
a:=[2]int{1,2} b:=a b[0]=999 fmt.Println("a:",a) // a: [1 2] fmt.Println("b:",b) // b: [999 2] 數(shù)組長(zhǎng)度是數(shù)組類型的組成部分,[1]int和[2]int表示不同的類型
-
可以根據(jù)數(shù)組創(chuàng)建切片,切片引用的是原數(shù)組,切片的改變會(huì)引起原數(shù)組的改變
a:=[2]int{1,2} b:=a[:] b[0]=999 fmt.Println("a:",a) // a: [999 2] fmt.Println("b:",b) // b: [999 2]
遍歷數(shù)組
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])
}
切片
因?yàn)閿?shù)組的長(zhǎng)度固定,而且是值拷貝,所以限制了數(shù)組的使用場(chǎng)景。此時(shí)切片(slice)應(yīng)運(yùn)而生,它是一種變長(zhǎng)數(shù)組,它是一種引用類型(map、channel也是引用類型)。
slice的底層如下:
type slice struct {
array unsafe.Pointer // 指向底層數(shù)組的指針
len int // 切片的元素?cái)?shù)量
cap int // 底層數(shù)組容量
}
創(chuàng)建方式
-
由數(shù)組創(chuàng)建:array[s:e],array是數(shù)組名,s是起始索引,e是終止索引,array[s:e]包括s索引下的數(shù)據(jù),但不包括e索引下的數(shù)據(jù)(相當(dāng)于左閉右開(kāi)[s,e)),也就是說(shuō)第一個(gè)元素的索引是s,最后一個(gè)元素的索引是e-1,該切片的長(zhǎng)度為e-s
var array = [3]int{1,2,3} a1:=array[:] // [1 2 3],如果不指定起始或者終止索引的話,起始索引默認(rèn)為0,終止索引默認(rèn)為len(array) a2:=array[1:2] // [2]同樣方式也可以通過(guò)切片創(chuàng)造切片
s:=[]int{1,2,3} b:=s[1:2] //[2]
-
由內(nèi)置函數(shù)make創(chuàng)建
通過(guò)make創(chuàng)建的切片的元素初始值都被默認(rèn)初始化為元素類型的零值
arr1:=make([]int,5) // [0 0 0 0 0] len=5,cap=5,所有元素都被默認(rèn)初始化為0 arr2:=make([]int,5,10) // [0 0 0 0 0] len=5,cap=10,所有元素都被默認(rèn)初始化為0 -
由字面量初始化
arr:=[]int{1,2,3} // [1 2 3] len=3 cap=3
常見(jiàn)操作
s:=[]int{1,2}
fmt.Println(len(s)) // len=2,返回切片長(zhǎng)度
fmt.Println(cap(s)) // cap=2,返回切片容量
s=append(s,3) // 向s追加元素;切片元素:[1,2,3];len=3,cap=4,切片長(zhǎng)度超過(guò)原來(lái)的容量,自動(dòng)擴(kuò)容,容量擴(kuò)大為原來(lái)的兩倍
s1:=[]int{4,5}
s=append(s,s1...) // 向s追加切片
fmt.Println(s) // [1 2 3 4 5]
s=append(s,7,8) // 向切片追加多個(gè)元素
fmt.Println(s) // [1 2 3 4 5 7 8]
c:=[]int{1,2,3}
b:=make([]int,2) // [0 0]
copy(b,c) //只會(huì)復(fù)制長(zhǎng)度較小的,第一個(gè)參數(shù)是目的切片,第二個(gè)參數(shù)是源切片
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:
當(dāng)切片的長(zhǎng)度超過(guò)容量cap的時(shí)候,切片會(huì)進(jìn)行擴(kuò)容,cap直接擴(kuò)大為原來(lái)的2倍。
-
append函數(shù)定義如下,第一個(gè)參數(shù)表示向哪個(gè)切片追加元素,后面的是可變參數(shù),可以添加多個(gè)參數(shù),也可以添加一個(gè)切片,當(dāng)?shù)诙€(gè)參數(shù)是切片時(shí),需要在切片后面添加三個(gè)點(diǎn)來(lái)表示引用切片中所有元素"..."
注意:可變參數(shù)可以是單個(gè)元素,也可以是一個(gè)切片…,但不能兩者同時(shí)出現(xiàn)。
func append(slice []Type, elems ...Type) []Type
s:=[]int{1,2}
s1:=[]int{4,5}
// s=append(s,1,s1...) // 報(bào)錯(cuò)
// s=append(s,s1...,1) // 報(bào)錯(cuò)
字符串和切片相互轉(zhuǎn)換
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:字符串轉(zhuǎn)切片的時(shí)候需要注意,尤其當(dāng)字符串內(nèi)容很大的時(shí)候,因?yàn)槊恳淮味家獜?fù)制內(nèi)容
刁鉆難點(diǎn)
- 通過(guò)var b []int創(chuàng)建切片,該切片底層array指針不指向任何數(shù)組(也就是array的值為nil),len、cap都是0;但通過(guò)a:=[]int{}這種方式創(chuàng)建的切片,雖然len、cap也都是0,但是底層array是開(kāi)辟了空間的,不為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
-
通過(guò)array[s:e]創(chuàng)建切片的時(shí)候,其實(shí)還可以這樣array[s:e:c],c的最大值是數(shù)組的長(zhǎng)度或者切片的容量的最大值。e-s是該切片的長(zhǎng)度,c-s是該切片的容量
a:=[]int{1,2} b:=a[0:1:2] fmt.Println(b,len(b),cap(b)) // [1] 1 2