第八章:Go語(yǔ)言切片slice

golang-gopher.png

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)存中的布局

slice.png

上圖中 ptr表示地址指針, len表示長(zhǎng)度, cap表示容量

2. 創(chuàng)建切片

切片的創(chuàng)建通常有兩種方式

  1. 基于某個(gè)數(shù)組創(chuàng)建切片
  2. 直接創(chuàng)建切片
  3. 使用內(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ō)明 :

  1. 內(nèi)置函數(shù)make()創(chuàng)建的切片,可以自己指定切片大小和容量 ,若是指定的切片容量就必須不小于切片的大小,否則編譯報(bào)錯(cuò)
  2. make()創(chuàng)建的切片沒(méi)有給元素賦值,那么就是自動(dòng)采用類(lèi)型默認(rèn)值
  3. 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ù)組的遍歷一樣,通常為兩種方式

  1. for 循環(huán)遍歷
  2. 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)容覆蓋
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乎澄,一起剝皮案震驚了整個(gè)濱河市蔗蹋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纺讲,老刑警劉巖茧跋,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捕虽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)坡脐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)泄私,“玉大人,你說(shuō)我怎么就攤上這事备闲∩味耍” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵恬砂,是天一觀的道長(zhǎng)咧纠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)泻骤,這世上最難降的妖魔是什么漆羔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮狱掂,結(jié)果婚禮上演痒,老公的妹妹穿的比我還像新娘。我一直安慰自己趋惨,他們只是感情好鸟顺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著器虾,像睡著了一般讯嫂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兆沙,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天端姚,我揣著相機(jī)與錄音,去河邊找鬼挤悉。 笑死渐裸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的装悲。 我是一名探鬼主播昏鹃,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诀诊!你這毒婦竟也來(lái)了洞渤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤属瓣,失蹤者是張志新(化名)和其女友劉穎载迄,沒(méi)想到半個(gè)月后讯柔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡护昧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年魂迄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惋耙。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捣炬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绽榛,到底是詐尸還是另有隱情湿酸,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布灭美,位于F島的核電站推溃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏届腐。R本人自食惡果不足惜美莫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梯捕。 院中可真熱鬧,春花似錦窝撵、人聲如沸傀顾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)短曾。三九已至,卻和暖如春赐劣,著一層夾襖步出監(jiān)牢的瞬間嫉拐,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工魁兼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留婉徘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓咐汞,卻偏偏與公主長(zhǎng)得像盖呼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子化撕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容