1. 關(guān)于切片的概述
切片slice是一個(gè)可變長(zhǎng)度的序列,切片中的每個(gè)元素都是相同類(lèi)型,我們可以將切片看做
動(dòng)態(tài)數(shù)組
- 切片是引用類(lèi)型,是對(duì)數(shù)組的引用
- 切片的大小是動(dòng)態(tài)分配的
- 切片的遍歷和訪(fǎng)問(wèn)切片元素操作和數(shù)組類(lèi)似
package main
import "fmt"
func main(){
var demo = []int{1,2,3,4,5,6,7}
fmt.Println(len(demo),cap(demo))
child := demo[2:4]
fmt.Println(child,len(child),cap(child))
}
go run main.go
7 7
[3 4] 2 5
為什么是這樣的呢?我們模擬一下在內(nèi)存中的布局
上圖中 ptr表示地址指針, len表示長(zhǎng)度, cap表示容量
2. 創(chuàng)建切片
切片的創(chuàng)建通常有兩種方式
- 基于某個(gè)數(shù)組創(chuàng)建切片
- 直接創(chuàng)建切片
- 使用內(nèi)置函數(shù)make()創(chuàng)建切片
2.1 切片的聲明
var name []T
- name 表示切片的變量名
- T 表示切片類(lèi)型對(duì)應(yīng)的元素類(lèi)型
切片只是聲明,并且沒(méi)有初始化那么切片還未分配內(nèi)存,默認(rèn)值是nil
package main
import "fmt"
func main(){
//聲明一個(gè)整型切片
var nSlice []int
//聲明一個(gè)字符串切片
var sSlice []string
//聲明一個(gè)字符串類(lèi)型的空切片
var sEmptySlice = []string{}
fmt.Println(nSlice,sSlice,sEmptySlice)
// 切片長(zhǎng)度都是0
fmt.Println(len(nSlice),len(sSlice),len(sEmptySlice))
// 未初始化的切片默認(rèn)值是nil
fmt.Println(nSlice == nil)
fmt.Println(sSlice == nil)
// 空切片也是分配的內(nèi)存,默認(rèn)值不是nil
fmt.Println(sEmptySlice == nil)
}
$ go run main.go
[] [] []
0 0 0
true
true
false
2.2 基于數(shù)組創(chuàng)建切片
:star: 切片slice 操作符s[i:j] (0<=i<=j<=cap(s)) 創(chuàng)建一個(gè)新的slice ,新的slice引用了序列s中從i到j(luò)-1索引位置的所有元素,這個(gè)s可以是數(shù)組可以是指向數(shù)組的指針,可以是slice,新的slice的元素個(gè)數(shù)是j-i個(gè),如果省略了i,那么新的slice其實(shí)索引位置是0,如果省略了j ,那么新slice結(jié)束索引位置是len(s)-1,就是 j = len(s)
package main
import "fmt"
func main() {
// 初始化一個(gè)數(shù)組
var nArr = [10]int{0,1,2,3,4,5,6,7,8,9}
s := nArr[2:8]
// s 是一個(gè)int類(lèi)型的切片
fmt.Printf("s 的類(lèi)型是 = %T\n",s)
fmt.Printf("s 的元素是 = %v\n",s)
fmt.Printf("s 的元素個(gè)數(shù)是 = %d\n",len(s))
fmt.Printf("s 的容量是 = %d\n",cap(s))
}
$ go run main.go
s 的類(lèi)型是 = []int
s 的元素是 = [2 3 4 5 6 7]
s 的元素個(gè)數(shù)是 = 6
s 的容量是 = 8
2.3 直接創(chuàng)建
package main
import "fmt"
func main(){
var nSlice = []int{1,2,3,4,5}
fmt.Printf("nSlice的長(zhǎng)度是%d,容量數(shù)%d,內(nèi)容是%v",len(nSlice),cap(nSlice),nSlice)
}
$ go run main.go
nSlice的長(zhǎng)度是5,容量數(shù)5,內(nèi)容是[1 2 3 4 5]
2.4 make()創(chuàng)建
格式
make([]T,size,cap)
- T : 切片類(lèi)型
- size : 切片分配的元素個(gè)數(shù)
- cap : 切片的容量
說(shuō)明 :
- 內(nèi)置函數(shù)make()創(chuàng)建的切片,可以自己指定切片大小和容量 ,若是指定的切片容量就必須不小于切片的大小,否則編譯報(bào)錯(cuò)
- make()創(chuàng)建的切片沒(méi)有給元素賦值,那么就是自動(dòng)采用類(lèi)型默認(rèn)值
- make()創(chuàng)建的切片對(duì)應(yīng)的數(shù)組由make底層維護(hù),外部不可見(jiàn)
package main
import "fmt"
func main(){
var mSlice1 []string = make([]string,3,5)
fmt.Printf("mSlice1的長(zhǎng)度是%d,容量數(shù)%d,內(nèi)容是%v\n", len(mSlice1), cap(mSlice1), mSlice1)
var mSlice2 []string = make([]string,3,3)
mSlice2[0] = "tom"
mSlice2[1] = "kobe"
mSlice2[2] = "jack"
fmt.Printf("mSlice2的長(zhǎng)度是%d,容量數(shù)%d,內(nèi)容是%v\n", len(mSlice2), cap(mSlice2), mSlice2)
}
$ go run main.go
mSlice1的長(zhǎng)度是3,容量數(shù)5,內(nèi)容是[ ]
mSlice2的長(zhǎng)度是3,容量數(shù)3,內(nèi)容是[tom kobe jack]
2.5 基于切片創(chuàng)建切片
package main
import "fmt"
func main(){
var oldS []int= make([]int,3,10)
fmt.Printf("切片oldS的長(zhǎng)度是%d,容量是%d,內(nèi)容是 %v\n", len(oldS), cap(oldS), oldS)
var newS = oldS[:6]
fmt.Printf("切片newS的長(zhǎng)度是%d,容量是%d,內(nèi)容是 %v\n", len(newS), cap(newS), newS)
}
$ go run main.go
切片oldS的長(zhǎng)度是3,容量是10,內(nèi)容是 [0 0 0]
切片newS的長(zhǎng)度是6,容量是10,內(nèi)容是 [0 0 0 0 0 0]
3. 對(duì)切片的操作
3.1 對(duì)切片添加元素
Go語(yǔ)言的內(nèi)置函數(shù)append() 可以為切片動(dòng)態(tài)添加元素,切片指向一片內(nèi)存空間,可以空間能容納一定數(shù)量的元素,當(dāng)空間不能容納足夠多的元素時(shí),切片就會(huì)進(jìn)行擴(kuò)容,這個(gè)操作一般發(fā)生在append()函數(shù)調(diào)用的時(shí)候
當(dāng)切片擴(kuò)容擴(kuò)容的時(shí)候切片的容量按2的倍數(shù)擴(kuò)充
- len()函數(shù)是參看切片擁有的元素個(gè)數(shù)
- cap()函數(shù)是查看切片容量的情況,"擴(kuò)容"是容量的增擴(kuò)
package main
import "fmt"
func printSlice(n []int){
// 觀察slice 的長(zhǎng)度和容量變化
fmt.Printf("切片n的長(zhǎng)度是%d,容量是%d,內(nèi)存地址是 %p\n",len(n),cap(n),&n)
}
func main(){
// 定義一個(gè)切片
var n []int
// 打印切片信息
printSlice(n)
// 給切片添加18個(gè)元素
for i:=0;i<18;i++{
n = append(n,i)
printSlice(n)
}
}
切片n的長(zhǎng)度是0,容量是0,內(nèi)存地址是 0xc000004480
切片n的長(zhǎng)度是1,容量是1,內(nèi)存地址是 0xc0000044c0
切片n的長(zhǎng)度是2,容量是2,內(nèi)存地址是 0xc0000044e0
切片n的長(zhǎng)度是3,容量是4,內(nèi)存地址是 0xc000004500
切片n的長(zhǎng)度是4,容量是4,內(nèi)存地址是 0xc000004520
切片n的長(zhǎng)度是5,容量是8,內(nèi)存地址是 0xc000004540
切片n的長(zhǎng)度是6,容量是8,內(nèi)存地址是 0xc000004560
切片n的長(zhǎng)度是7,容量是8,內(nèi)存地址是 0xc000004580
切片n的長(zhǎng)度是8,容量是8,內(nèi)存地址是 0xc0000045a0
切片n的長(zhǎng)度是9,容量是16,內(nèi)存地址是 0xc0000045c0
切片n的長(zhǎng)度是10,容量是16,內(nèi)存地址是 0xc0000045e0
切片n的長(zhǎng)度是11,容量是16,內(nèi)存地址是 0xc000004600
切片n的長(zhǎng)度是12,容量是16,內(nèi)存地址是 0xc000004620
切片n的長(zhǎng)度是13,容量是16,內(nèi)存地址是 0xc000004640
切片n的長(zhǎng)度是14,容量是16,內(nèi)存地址是 0xc000004660
切片n的長(zhǎng)度是15,容量是16,內(nèi)存地址是 0xc000004680
切片n的長(zhǎng)度是16,容量是16,內(nèi)存地址是 0xc0000046a0
切片n的長(zhǎng)度是17,容量是32,內(nèi)存地址是 0xc0000046c0
切片n的長(zhǎng)度是18,容量是32,內(nèi)存地址是 0xc0000046e0
append()方法可以一次性添加多個(gè)元素到切片中
package main
import "fmt"
func main(){
var s []string = []string{"a"}
fmt.Printf("切片n的長(zhǎng)度是%d,容量是%d,內(nèi)容是 %v\n",len(s),cap(s),s)
s =append(s,"b","c","d","e")
fmt.Printf("切片n的長(zhǎng)度是%d,容量是%d,內(nèi)容是 %v\n",len(s),cap(s),s)
}
$ go run main.go
切片n的長(zhǎng)度是1,容量是1,內(nèi)容是 [a]
切片n的長(zhǎng)度是5,容量是5,內(nèi)容是 [a b c d e]
append()可以將同類(lèi)的切片追加到另一個(gè)切片后面
package main
import "fmt"
func main(){
var s []string = []string{"a","b","c"}
fmt.Printf("切片n的長(zhǎng)度是%d,容量是%d,內(nèi)容是 %v\n", len(s), cap(s), s)
var s1 []string = []string{"w","x","y","z"}
//注意這個(gè)寫(xiě)法,要是直接寫(xiě)s1報(bào)錯(cuò) `cannot use s1 (type []string) as type string in append`
s = append(s,s1...)
fmt.Printf("切片n的長(zhǎng)度是%d,容量是%d,內(nèi)容是 %v\n", len(s), cap(s), s)
}
$ go run main.go
切片s的長(zhǎng)度是3,容量是3,內(nèi)容是 [a b c]
切片s的長(zhǎng)度是7,容量是7,內(nèi)容是 [a b c w x y z]
3.2 遍歷切片
切片的遍歷與數(shù)組的遍歷一樣,通常為兩種方式
- for 循環(huán)遍歷
- for -range 結(jié)構(gòu)遍歷
package main
import "fmt"
func main(){
var arr = [...]int{100,2000,300,40,58,6,7,8,9,0,11,12,13}
s := arr[:9]
L := len(s)
// 使用for循環(huán)遍歷slice
for i:=0 ;i<L ;i++{
fmt.Printf("%d\t",s[i])
}
fmt.Println("");
// 使用for-range 結(jié)構(gòu)遍歷切片
for _,v := range s{
fmt.Printf("%d\t",v)
}
}
$ go run main.go
100 2000 300 40 58 6 7 8 9
100 2000 300 40 58 6 7 8 9
3.3 刪除切片中的元素
Go語(yǔ)言中沒(méi)有專(zhuān)門(mén)的語(yǔ)法或者函數(shù)方法去刪除切片中的元素,實(shí)現(xiàn)刪除切片中的元素需要借助切片本身的 特點(diǎn)來(lái)實(shí)現(xiàn)
在Go語(yǔ)言中刪除切片中元素,實(shí)際上就是以被刪除元素為分界點(diǎn),將前后兩部分再重新連接起來(lái)
package main
import "fmt"
func main() {
// 定義一個(gè)切片
var s = []int{100, 2000, 300, 40, 58, 6, 7, 8, 9, 0, 11, 12, 13}
fmt.Printf("len = %d,cap = %d\n%v\n",len(s),cap(s),s)
// 刪除指定的元素
delIndex := 2
fmt.Println(s[:delIndex]," ",s[delIndex+1:])
s = append(s[:delIndex],s[delIndex+1:]...)
fmt.Printf("len = %d,cap = %d\n%v\n",len(s),cap(s),s)
}
$ go run main.go
len = 13,cap = 13
[100 2000 300 40 58 6 7 8 9 0 11 12 13]
[100 2000] [40 58 6 7 8 9 0 11 12 13]
len = 12,cap = 13
[100 2000 40 58 6 7 8 9 0 11 12 13]
3.5 切片復(fù)制
在Go語(yǔ)言中內(nèi)建函數(shù)copy() ,可以迅速的將一個(gè)切片的數(shù)據(jù)復(fù)制到另外一個(gè)切片空間中
copy()函數(shù)的格式
func copy(dst, src []Type) int
- dst 是目標(biāo) 切片
- src 是源切片,就是將src的元素復(fù)制到dst切片中
- 執(zhí)行該函數(shù)后返回的是實(shí)際發(fā)生復(fù)制的元素個(gè)數(shù)
package main
import "fmt"
func main(){
var s1 []int = []int{1,2,3,4,5}
var s2 []int = []int{7,8,9}
var s3 []string = []string{"a","b","c","d","e"}
var s4 []string = []string{"x","y"}
var s5 []string = []string{"C","java","javascript","golang"}
var s6 []string = []string{"C語(yǔ)言","java語(yǔ)言","javascript語(yǔ)言","Go語(yǔ)言"}
n1 := copy(s2,s1)
n2 := copy(s3,s4)
n3 := copy(s5,s6)
fmt.Printf("復(fù)制的元素個(gè)數(shù)%d,s2 = %v\n",n1,s2)
fmt.Printf("復(fù)制的元素個(gè)數(shù)%d,s3 = %v\n",n2,s3)
fmt.Printf("復(fù)制的元素個(gè)數(shù)%d,s5 = %v\n",n3,s5)
}
$ go run main.go
復(fù)制的元素個(gè)數(shù)3,s2 = [1 2 3]
復(fù)制的元素個(gè)數(shù)2,s3 = [x y c d e]
復(fù)制的元素個(gè)數(shù)4,s5 = [C語(yǔ)言 java語(yǔ)言 javascript語(yǔ)言 Go語(yǔ)言]
切片復(fù)制總結(jié) :
- 使用copy()實(shí)現(xiàn)切片復(fù)制的目標(biāo)切片和源切片必須是具有相同的元素類(lèi)型
- 長(zhǎng)度相等的兩個(gè)切片復(fù)制,源切片的元素會(huì)全部復(fù)制給目標(biāo)切片,此時(shí)目標(biāo)切片的內(nèi)容和源切片完全一致
- 元素長(zhǎng)度不等的兩個(gè)切片進(jìn)行復(fù)制,復(fù)制的元素個(gè)數(shù)以小的切片為準(zhǔn)
- 如果目標(biāo)切片長(zhǎng)度大于原切片長(zhǎng)度,那么源切片的所有元素,都復(fù)制給目標(biāo)切片索引相同的位置
- 如果目標(biāo)切片長(zhǎng)度小于源切片長(zhǎng)度,那么目標(biāo)切片相同索引位置的元素內(nèi)容,被源切片相同索引位置的內(nèi)容覆蓋