切片也是一種數(shù)據(jù)結(jié)構(gòu),它和數(shù)組非常相似,因?yàn)樗菄@動(dòng)態(tài)數(shù)組的概念設(shè)計(jì)的,可以按需自動(dòng)改變大小,使用這種結(jié)構(gòu),可以更方便地管理和使用數(shù)據(jù)集合。
創(chuàng)新互聯(lián)公司2013年成立,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站制作、網(wǎng)站設(shè)計(jì)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元黃州做網(wǎng)站,已為上家服務(wù),為黃州各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話(huà):18980820575
內(nèi)部實(shí)現(xiàn)
切片是基于數(shù)組實(shí)現(xiàn)的,它的底層是數(shù)組,它自己本身非常小,可以理解為對(duì)底層數(shù)組的抽象。因?yàn)闄C(jī)遇數(shù)組實(shí)現(xiàn),所以它的底層的內(nèi)存是連續(xù)非配的,效率非常高。它還有可以通過(guò)索引獲得數(shù)據(jù)、可以迭代以及垃圾回收優(yōu)化的好處。
切片對(duì)象非常小,是因?yàn)樗侵挥?3 個(gè)字段的數(shù)據(jù)結(jié)構(gòu):一個(gè)是指向底層數(shù)組的指針,一個(gè)是切片的長(zhǎng)度,一個(gè)是切片的容量。這 3 個(gè)字段,就是Go語(yǔ)言操作底層數(shù)組的元數(shù)據(jù),有了它們,我們就可以任意地操作切片了。
聲明和初始化
切片創(chuàng)建的方式有好幾種,我們先看下最簡(jiǎn)潔的make方式。
slice:=make([]int,5)
使用內(nèi)置的make
函數(shù)時(shí),需要傳入一個(gè)參數(shù),指定切片的長(zhǎng)度,例子中我們使用的時(shí) 5 ,這時(shí)候切片的容量也是 5 。當(dāng)然我們也可以單獨(dú)指定切片的容量。
slice:=make([]int,5,10)
這時(shí),我們創(chuàng)建的切片長(zhǎng)度是 5 ,容量是 10 。需要注意的這個(gè)容量 10 其實(shí)對(duì)應(yīng)的是切片底層數(shù)組的。
因?yàn)榍衅牡讓邮菙?shù)組,所以創(chuàng)建切片時(shí),如果不指定字面值的話(huà),默認(rèn)值就是數(shù)組的元素的零值。這里我們所以指定了容量是 10 ,但是我們只能訪(fǎng)問(wèn) 5 個(gè)元素。因?yàn)榍衅拈L(zhǎng)度是 5 ,剩下的 5 個(gè)元素,需要切片擴(kuò)充后才可以訪(fǎng)問(wèn)。
容量必須>=長(zhǎng)度,我們是不能創(chuàng)建長(zhǎng)度大于容量的切片的。
還有一種創(chuàng)建切片的方式,是使用字面量,就是指定初始化的值。
slice:=[]int{1,2,3,4,5}
有沒(méi)有發(fā)現(xiàn),跟創(chuàng)建數(shù)組非常像,只不過(guò)不用制定[]
中的值。這時(shí)候切片的長(zhǎng)度和容量是相等的,并且會(huì)根據(jù)我們指定的字面量推導(dǎo)出來(lái)。當(dāng)然我們也可以像數(shù)組一樣,只初始化某個(gè)索引的值:
slice:=[]int{4:1}
這是指定了第 5 個(gè)元素為 1 ,其他元素都是默認(rèn)值 0 。這時(shí)候切片的長(zhǎng)度和容量也是一樣的。這里再次強(qiáng)調(diào)一下切片和數(shù)組的微小差別。
//數(shù)組 array:=[5]int{4:1} //切片 slice:=[]int{4:1}
切片還有nil切片和空切片,它們的長(zhǎng)度和容量都是 0 。但是它們指向底層數(shù)組的指針不一樣,nil切片意味著指向底層數(shù)組的指針為nil,而空切片對(duì)應(yīng)的指針是個(gè)地址。
//nil切片 var nilSlice []int //空切片 slice:=[]int{}
nil切片表示不存在的切片,而空切片表示一個(gè)空集合,它們各有用處。
切片另外一個(gè)用處比較多的創(chuàng)建是基于現(xiàn)有的數(shù)組或者切片創(chuàng)建。
slice := []int{1, 2, 3, 4, 5} slice1 := slice[:] slice2 := slice[0:] slice3 := slice[:5] fmt.Println(slice1) fmt.Println(slice2) fmt.Println(slice3)
基于現(xiàn)有的切片或者數(shù)組創(chuàng)建,使用[i:j]
這樣的操作符即可。它表示以i
索引開(kāi)始,到j
索引結(jié)束,截取原數(shù)組或者切片,創(chuàng)建而成的新切片。新切片的值包含原切片的i
索引,但是不包含j
索引。對(duì)比Java的話(huà),發(fā)現(xiàn)和String的subString
方法很像。
i
如果省略,默認(rèn)是 0 ;j
如果省略,默認(rèn)是原數(shù)組或者切片的長(zhǎng)度。所以例子中的三個(gè)新切片的值是一樣的。這里注意的是i
和j
都不能超過(guò)原切片或者數(shù)組的索引。
slice := []int{1, 2, 3, 4, 5} newSlice := slice[1:3] newSlice[0] = 10 fmt.Println(slice) fmt.Println(newSlice)
這個(gè)例子證明了,新的切片和原切片共用的是一個(gè)底層數(shù)組。所以當(dāng)修改的時(shí)候,底層數(shù)組的值就會(huì)被改變,所以原切片的值也改變了。當(dāng)然對(duì)于基于數(shù)組的切片也一樣的。
我們基于原數(shù)組或者切片創(chuàng)建一個(gè)新的切片后,那么新的切片的大小和容量是多少呢?這里有個(gè)公式:
對(duì)于底層數(shù)組容量是k的切片slice[i:j]來(lái)說(shuō) 長(zhǎng)度:j-i 容量:k-i
比如我們上面的例子slice[1:3]
,長(zhǎng)度就是3-1=2
,容量是5-1=4
。不過(guò)代碼中我們計(jì)算的時(shí)候不用這么麻煩,因?yàn)镚o語(yǔ)言為我們提供了內(nèi)置的len
和cap
函數(shù)來(lái)計(jì)算切片的長(zhǎng)度和容量。
slice := []int{1, 2, 3, 4, 5} newSlice := slice[1:3] fmt.Printf("newSlice長(zhǎng)度:%d,容量:%d",len(newSlice),cap(newSlice))
以上是基于一個(gè)數(shù)組或者切片使用 2 個(gè)索引創(chuàng)建新切片的方法。此外還有一種 3 個(gè)索引的方法,第 3 個(gè)用來(lái)限定新切片的容量,其用法為slice[i:j:k]
。
slice := []int{1, 2, 3, 4, 5} newSlice := slice[1:2:3]
這樣我們就創(chuàng)建了一個(gè)長(zhǎng)度為2-1=1
,容量為3-1=2
的新切片,不過(guò)第三個(gè)索引,不能超過(guò)原切片的最大索引值 5 。
使用切片
使用切片,和使用數(shù)組一樣,通過(guò)索引就可以獲取切片對(duì)應(yīng)元素的值,同樣也可以修改對(duì)應(yīng)元素的值。
slice := []int{1, 2, 3, 4, 5} fmt.Println(slice[2]) //獲取值 slice[2] = 10 //修改值 fmt.Println(slice[2]) //輸出10
切片只能訪(fǎng)問(wèn)到其長(zhǎng)度內(nèi)的元素,訪(fǎng)問(wèn)超過(guò)長(zhǎng)度外的元素,會(huì)導(dǎo)致運(yùn)行時(shí)異常,與切片容量關(guān)聯(lián)的元素只能用于切片增長(zhǎng)。
我們前面講了,切片算是一個(gè)動(dòng)態(tài)數(shù)組,所以它可以按需增長(zhǎng),我們使用內(nèi)置append
函數(shù)即可。append
函數(shù)可以為一個(gè)切片追加一個(gè)元素,至于如何增加、返回的是原切片還是一個(gè)新切片、長(zhǎng)度和容量如何改變這些細(xì)節(jié),append
函數(shù)都會(huì)幫我們自動(dòng)處理。
slice := []int{1, 2, 3, 4, 5} newSlice := slice[1:3] newSlice=append(newSlice,10) fmt.Println(newSlice) fmt.Println(slice) //Output [2 3 10] [1 2 3 10 5]
例子中,通過(guò)append
函數(shù)為新創(chuàng)建的切片newSlice
,追加了一個(gè)元素 10 。我們發(fā)現(xiàn)打印的輸出,原切片slice
的第 4 個(gè)值也被改變了,變成了 10 。引起這種結(jié)果的原因是因?yàn)?code>newSlice有可用的容量,不會(huì)創(chuàng)建新的切片來(lái)滿(mǎn)足追加,所以直接在newSlice
后追加了一個(gè)元素 10 。因?yàn)?code>newSlice和slice
切片共用一個(gè)底層數(shù)組,所以切片slice
的對(duì)應(yīng)的元素值也被改變了。
這里newSlice新追加的第 3 個(gè)元素,其實(shí)對(duì)應(yīng)的是slice的第 4 個(gè)元素,所以這里的追加其實(shí)是把底層數(shù)組的第4個(gè)元素修改為 10 ,然后把newSlice長(zhǎng)度調(diào)整為 3 。
如果切片的底層數(shù)組沒(méi)有足夠的容量時(shí),就會(huì)新建一個(gè)底層數(shù)組,把原來(lái)數(shù)組的值復(fù)制到新底層數(shù)組里,再追加新值,這時(shí)候就不會(huì)影響原來(lái)的底層數(shù)組了。
所以一般我們?cè)趧?chuàng)建新切片的時(shí)候,最好要讓新切片的長(zhǎng)度和容量一樣,這樣我們?cè)谧芳硬僮鞯臅r(shí)候就會(huì)生成新的底層數(shù)組,和原有數(shù)組分離,就不會(huì)因?yàn)楣灿玫讓訑?shù)組而引起奇怪問(wèn)題,因?yàn)楣灿脭?shù)組的時(shí)候修改內(nèi)容,會(huì)影響多個(gè)切片。
append
函數(shù)會(huì)智能地增長(zhǎng)底層數(shù)組的容量,目前的算法是:容量小于 1000 個(gè)時(shí),總是成倍的增長(zhǎng);一旦容量超過(guò) 1000 個(gè),增長(zhǎng)因子設(shè)為 1.25 ,也就是說(shuō)每次會(huì)增加 25% 的容量。
內(nèi)置的append
也是一個(gè)可變參數(shù)的函數(shù),所以我們可以同時(shí)追加好幾個(gè)值。
newSlice=append(newSlice,10,20,30)
此外,我們還可以通過(guò)...
操作符,把一個(gè)切片追加到另一個(gè)切片里。
slice := []int{1, 2, 3, 4, 5} newSlice := slice[1:2:3] newSlice=append(newSlice,slice...) fmt.Println(newSlice) fmt.Println(slice)
迭代切片
切片是一個(gè)集合,我們可以使用for range循環(huán)來(lái)迭代它,打印其中的每個(gè)元素以及對(duì)應(yīng)的索引。
slice := []int{1, 2, 3, 4, 5} for i,v:=range slice{ fmt.Printf("索引:%d,值:%d\n",i,v) }
如果我們不想要索引,可以使用_
來(lái)忽略它。這是Go語(yǔ)言的用法,很多不需要的函數(shù)等返回值,都可以忽略。
slice := []int{1, 2, 3, 4, 5} for _,v:=range slice{ fmt.Printf("值:%d\n",v) }
這里需要說(shuō)明的是range
返回的是切片元素的復(fù)制,而不是元素的引用。
除了for range循環(huán)外,我們也可以使用傳統(tǒng)的for循環(huán),配合內(nèi)置的len函數(shù)進(jìn)行迭代。
slice := []int{1, 2, 3, 4, 5}
for i := 0; i < len(slice); i++ {
fmt.Printf("值:%d\n", slice[i])
}
在函數(shù)間傳遞切片
我們知道切片是 3 個(gè)字段構(gòu)成的結(jié)構(gòu)類(lèi)型,所以在函數(shù)間以值的方式傳遞的時(shí)候,占用的內(nèi)存非常小,成本很低。在傳遞復(fù)制切片的時(shí)候,其底層數(shù)組不會(huì)被復(fù)制,也不會(huì)受影響,復(fù)制只是復(fù)制的切片本身,不涉及底層數(shù)組。
func main() { slice := []int{1, 2, 3, 4, 5} fmt.Printf("%p\n", &slice) modify(slice) fmt.Println(slice) } func modify(slice []int) { fmt.Printf("%p\n", &slice) slice[1] = 10 }
打印的輸出如下:
0xc420082060 0xc420082080 [1 10 3 4 5]
仔細(xì)看,這兩個(gè)切片的地址不一樣,所以可以確認(rèn)切片在函數(shù)間傳遞是復(fù)制的。而我們修改一個(gè)索引的值后,發(fā)現(xiàn)原切片的值也被修改了,說(shuō)明它們共用一個(gè)底層數(shù)組。
在函數(shù)間傳遞切片非常高效,而且不需要傳遞指針和處理復(fù)雜的語(yǔ)法,只需要復(fù)制切片,然后根據(jù)自己的業(yè)務(wù)修改,最后傳遞回一個(gè)新的切片副本即可。這也是為什么函數(shù)間使用切片傳遞參數(shù),而不是數(shù)組的原因。
關(guān)于多維切片就不介紹了,還有多維數(shù)組,一來(lái)它和普通的切片數(shù)組一樣,只不過(guò)是多個(gè)一維組成的多維;二來(lái)我壓根不推薦用多維切片和數(shù)組,可讀性不好,結(jié)構(gòu)不夠清晰,容易出問(wèn)題。
當(dāng)前題目:Go語(yǔ)言之切片
新聞來(lái)源:http://m.newbst.com/article28/jpjgcp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)、網(wǎng)站策劃、定制網(wǎng)站、Google、外貿(mào)網(wǎng)站建設(shè)、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)