append的第二個(gè)參數(shù)是可變長(zhǎng)參數(shù),你這樣寫1,2 就會(huì)追加2個(gè)元素這符合預(yù)期效果
我們提供的服務(wù)有:成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、環(huán)縣ssl等。為上千家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的環(huán)縣網(wǎng)站制作公司
Go 中數(shù)組的長(zhǎng)度是不可改變的,而 Slice 解決的就是對(duì)不定長(zhǎng)數(shù)組的需求。他們的區(qū)別主要有兩點(diǎn)。
數(shù)組:
切片:
注意 1
雖然數(shù)組在初始化時(shí)也可以不指定長(zhǎng)度,但 Go 語(yǔ)言會(huì)根據(jù)數(shù)組中元素個(gè)數(shù)自動(dòng)設(shè)置數(shù)組長(zhǎng)度,并且不可改變。切片通過(guò) append 方法增加元素:
如果將 append 用在數(shù)組上,你將會(huì)收到報(bào)錯(cuò):first argument to append must be slice。
注意 2
切片不只有長(zhǎng)度(len)的概念,同時(shí)還有容量(cap)的概念。因此切片其實(shí)還有一個(gè)指定長(zhǎng)度和容量的初始化方式:
這就初始化了一個(gè)長(zhǎng)度為3,容量為5的切片。
此外,切片還可以從一個(gè)數(shù)組中初始化(可應(yīng)用于如何將數(shù)組轉(zhuǎn)換成切片):
上述例子通過(guò)數(shù)組 a 初始化了一個(gè)切片 s。
當(dāng)切片和數(shù)組作為參數(shù)在函數(shù)(func)中傳遞時(shí),數(shù)組傳遞的是值,而切片傳遞的是指針。因此當(dāng)傳入的切片在函數(shù)中被改變時(shí),函數(shù)外的切片也會(huì)同時(shí)改變。相同的情況,函數(shù)外的數(shù)組則不會(huì)發(fā)生任何變化。
、數(shù)組
與其他大多數(shù)語(yǔ)言類似,Go語(yǔ)言的數(shù)組也是一個(gè)元素類型相同的定長(zhǎng)的序列。
(1)數(shù)組的創(chuàng)建。
數(shù)組有3種創(chuàng)建方式:[length]Type 、[N]Type{value1, value2, ... , valueN}、[...]Type{value1, value2, ... , valueN} 如下:
復(fù)制代碼代碼如下:
func test5() {
var iarray1 [5]int32
var iarray2 [5]int32 = [5]int32{1, 2, 3, 4, 5}
iarray3 := [5]int32{1, 2, 3, 4, 5}
iarray4 := [5]int32{6, 7, 8, 9, 10}
iarray5 := [...]int32{11, 12, 13, 14, 15}
iarray6 := [4][4]int32{{1}, {1, 2}, {1, 2, 3}}
fmt.Println(iarray1)
fmt.Println(iarray2)
fmt.Println(iarray3)
fmt.Println(iarray4)
fmt.Println(iarray5)
fmt.Println(iarray6)
}
結(jié)果:
[0 0 0 0 0]
[1 2 3 4 5]
[1 2 3 4 5]
[6 7 8 9 10]
[11 12 13 14 15]
[[1 0 0 0] [1 2 0 0] [1 2 3 0] [0 0 0 0]]
我們看數(shù)組 iarray1,只聲明,并未賦值,Go語(yǔ)言幫我們自動(dòng)賦值為0。再看 iarray2 和 iarray3 ,我們可以看到,Go語(yǔ)言的聲明,可以表明類型,也可以不表明類型,var iarray3 = [5]int32{1, 2, 3, 4, 5} 也是完全沒(méi)問(wèn)題的。
(2)數(shù)組的容量和長(zhǎng)度是一樣的。cap() 函數(shù)和 len() 函數(shù)均輸出數(shù)組的容量(即長(zhǎng)度)。如:
復(fù)制代碼代碼如下:
func test6() {
iarray4 := [5]int32{6, 7, 8, 9, 10}
fmt.Println(len(iarray4))
fmt.Println(cap(iarray4))
}
輸出都是5。
(3)使用:
復(fù)制代碼代碼如下:
func test7() {
iarray7 := [5]string{"aaa", `bb`, "可以啦", "叫我說(shuō)什么好", "()"}
fmt.Println(iarray7)
for i := range iarray7 {
fmt.Println(iarray7[i])
}
}
二、切片
Go語(yǔ)言中,切片是長(zhǎng)度可變、容量固定的相同的元素序列。Go語(yǔ)言的切片本質(zhì)是一個(gè)數(shù)組。容量固定是因?yàn)閿?shù)組的長(zhǎng)度是固定的,切片的容量即隱藏?cái)?shù)組的長(zhǎng)度。長(zhǎng)度可變指的是在數(shù)組長(zhǎng)度的范圍內(nèi)可變。
(1)切片的創(chuàng)建。
切片的創(chuàng)建有4種方式:
1)make ( []Type ,length, capacity )
2) make ( []Type, length)
3) []Type{}
4) []Type{value1 , value2 , ... , valueN }
從3)、4)可見,創(chuàng)建切片跟創(chuàng)建數(shù)組唯一的區(qū)別在于 Type 前的“ [] ”中是否有數(shù)字,為空,則代表切片,否則則代表數(shù)組。因?yàn)榍衅情L(zhǎng)度可變的。如下是創(chuàng)建切片的示例:
復(fù)制代碼代碼如下:
func test8() {
slice1 := make([]int32, 5, 8)
slice2 := make([]int32, 9)
slice3 := []int32{}
slice4 := []int32{1, 2, 3, 4, 5}
fmt.Println(slice1)
fmt.Println(slice2)
fmt.Println(slice3)
fmt.Println(slice4)
}
輸出為:
[0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[]
[1 2 3 4 5]
如上,創(chuàng)造了4個(gè)切片,3個(gè)空切片,一個(gè)有值的切片。
(2)切片與隱藏?cái)?shù)組:
一個(gè)切片是一個(gè)隱藏?cái)?shù)組的引用,并且對(duì)于該切片的切片也引用同一個(gè)數(shù)組。如下示例,創(chuàng)建了一個(gè)切片slice0,并根據(jù)這個(gè)切片創(chuàng)建了2個(gè)切片 slice1 和 slice2:
復(fù)制代碼代碼如下:
func test9() {
slice0 := []string{"a", "b", "c", "d", "e"}
slice1 := slice0[2 : len(slice0)-1]
slice2 := slice0[:3]
fmt.Println(slice0, slice1, slice2)
slice2[2] = "8"
fmt.Println(slice0, slice1, slice2)
}
輸出為:
[a b c d e] [c d] [a b c]
[a b 8 d e] [8 d] [a b 8]
可見,切片slice0 、 slice1 和 slice2是同一個(gè)底層數(shù)組的引用,所以slice2改變了,其他兩個(gè)都會(huì)變。
(3)遍歷、修改切片:
復(fù)制代碼代碼如下:
func test10() {
slice0 := []string{"a", "b", "c", "d", "e"}
fmt.Println("\n~~~~~~元素遍歷~~~~~~")
for _, ele := range slice0 {
fmt.Print(ele, " ")
ele = "7"
}
fmt.Println("\n~~~~~~索引遍歷~~~~~~")
for index := range slice0 {
fmt.Print(slice0[index], " ")
}
fmt.Println("\n~~~~~~元素索引共同使用~~~~~~")
for index, ele := range slice0 {
fmt.Print(ele, slice0[index], " ")
}
fmt.Println("\n~~~~~~修改~~~~~~")
for index := range slice0 {
slice0[index] = "9"
}
fmt.Println(slice0)
}
如上,前三種循環(huán)使用了不同的for range循環(huán),當(dāng)for后面,range前面有2個(gè)元素時(shí),第一個(gè)元素代表索引,第二個(gè)元素代表元素值,使用 “_” 則表示忽略,因?yàn)間o語(yǔ)言中,未使用的值會(huì)導(dǎo)致編譯錯(cuò)誤。
只有一個(gè)元素時(shí),該元素代表索引。
只有用索引才能修改元素。如在第一個(gè)遍歷中,賦值ele為7,結(jié)果沒(méi)有作用。因?yàn)樵谠乇闅v中,ele是值傳遞,ele是該切片元素的副本,修改它不會(huì)影響原本值,而在第四個(gè)遍歷——索引遍歷中,修改的是該切片元素引用的值,所以可以修改。
結(jié)果為:
~~~~~~元素遍歷~~~~~~
a b c d e
~~~~~~索引遍歷~~~~~~
a b c d e
~~~~~~元素索引共同使用~~~~~~
aa bb cc dd ee
~~~~~~修改~~~~~~
[9 9 9 9 9]
(4)、追加、復(fù)制切片:
復(fù)制代碼代碼如下:
func test11() {
slice := []int32{}
fmt.Printf("slice的長(zhǎng)度為:%d,slice為:%v\n", len(slice), slice)
slice = append(slice, 12, 11, 10, 9)
fmt.Printf("追加后,slice的長(zhǎng)度為:%d,slice為:%v\n", len(slice), slice)
slicecp := make([]int32, (len(slice)))
fmt.Printf("slicecp的長(zhǎng)度為:%d,slicecp為:%v\n", len(slicecp), slicecp)
copy(slicecp, slice)
fmt.Printf("復(fù)制賦值后,slicecp的長(zhǎng)度為:%d,slicecp為:%v\n", len(slicecp), slicecp)
}
追加、復(fù)制切片,用的是內(nèi)置函數(shù)append和copy,copy函數(shù)返回的是最后所復(fù)制的元素的數(shù)量。
(5)、內(nèi)置函數(shù)append
內(nèi)置函數(shù)append可以向一個(gè)切片后追加一個(gè)或多個(gè)同類型的其他值。如果追加的元素?cái)?shù)量超過(guò)了原切片容量,那么最后返回的是一個(gè)全新數(shù)組中的全新切片。如果沒(méi)有超過(guò),那么最后返回的是原數(shù)組中的全新切片。無(wú)論如何,append對(duì)原切片無(wú)任何影響。如下示例:
復(fù)制代碼代碼如下:
func test12() {
slice := []int32{1, 2, 3, 4, 5, 6}
slice2 := slice[:2]
_ = append(slice2, 50, 60, 70, 80, 90)
fmt.Printf("slice為:%v\n", slice)
fmt.Printf("操作的切片:%v\n", slice2)
_ = append(slice2, 50, 60)
fmt.Printf("slice為:%v\n", slice)
fmt.Printf("操作的切片:%v\n", slice2)
}
如上,append方法用了2次,結(jié)果返回的結(jié)果完全不同,原因是第二次append方法追加的元素?cái)?shù)量沒(méi)有超過(guò) slice 的容量。而無(wú)論怎樣,原切片slice2都無(wú)影響。結(jié)果:
slice為:[1 2 3 4 5 6]
操作的切片:[1 2]
slice為:[1 2 50 60 5 6]
操作的切片:[1 2]
Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)中提供了sort包對(duì)整型,浮點(diǎn)型,字符串型切片進(jìn)行排序,檢查一個(gè)切片是否排好序,使用二分法搜索函數(shù)在一個(gè)有序切片中搜索一個(gè)元素等功能。
關(guān)于sort包內(nèi)的函數(shù)說(shuō)明與使用,請(qǐng)查看
在這里簡(jiǎn)單講幾個(gè)sort包中常用的函數(shù)
在Go語(yǔ)言中,對(duì)字符串的排序都是按照字節(jié)排序,也就是說(shuō)在對(duì)字符串排序時(shí)是區(qū)分大小寫的。
二分搜索算法
Go語(yǔ)言中提供了一個(gè)使用二分搜索算法的sort.Search(size,fn)方法:每次只需要比較㏒?n個(gè)元素,其中n為切片中元素的總數(shù)。
sort.Search(size,fn)函數(shù)接受兩個(gè)參數(shù):所處理的切片的長(zhǎng)度和一個(gè)將目標(biāo)元素與有序切片的元素相比較的函數(shù),該函數(shù)是一個(gè)閉包,如果該有序切片是升序排列,那么在判斷時(shí)使用 有序切片的元素 = 目標(biāo)元素。該函數(shù)返回一個(gè)int值,表示與目標(biāo)元素相同的切片元素的索引。
在切片中查找出某個(gè)與目標(biāo)字符串相同的元素索引
[5]int 是數(shù)組,而 []int 是切片。二者看起來(lái)相似,實(shí)則是根本上不同的數(shù)據(jù)結(jié)構(gòu)。
切片的數(shù)據(jù)結(jié)構(gòu)中,包含一個(gè)指向數(shù)組的指針 array ,當(dāng)前長(zhǎng)度 len ,以及最大容量 cap 。在使用 make([]int, len) 創(chuàng)建切片時(shí),實(shí)際上還有第三個(gè)可選參數(shù) cap ,也即 make([]int, len, cap) 。在不聲明 cap 的情況下,默認(rèn) cap=len 。當(dāng)切片長(zhǎng)度沒(méi)有超過(guò)容量時(shí),對(duì)切片新增數(shù)據(jù),不會(huì)改變 array 指針的值。
當(dāng)對(duì)切片進(jìn)行 append 操作,導(dǎo)致長(zhǎng)度超出容量時(shí),就會(huì)創(chuàng)建新的數(shù)組,這會(huì)導(dǎo)致和原有切片的分離。在下例中
由于 a 的長(zhǎng)度超出了容量,所以切片 a 指向了一個(gè)增長(zhǎng)后的新數(shù)組,而 b 仍然指向原來(lái)的老數(shù)組。所以之后對(duì) a 進(jìn)行的操作,對(duì) b 不會(huì)產(chǎn)生影響。
試比較
本例中, a 的容量為6,因此在 append 后并未超出容量,所以 array 指針沒(méi)有改變。因此,對(duì) a 進(jìn)行的操作,對(duì) b 同樣產(chǎn)生了影響。
下面看看用 a := []int{} 這種方式來(lái)創(chuàng)建切片會(huì)是什么情況。
可以看到,空切片的容量為0,但后面向切片中添加元素時(shí),并不是每次切片的容量都發(fā)生了變化。這是因?yàn)?,如果增大容量,也即需要?jiǎng)?chuàng)建新數(shù)組,這時(shí)還需要將原數(shù)組中的所有元素復(fù)制到新數(shù)組中,開銷很大,所以GoLang設(shè)計(jì)了一套擴(kuò)容機(jī)制,以減少需要?jiǎng)?chuàng)建新數(shù)組的次數(shù)。但這導(dǎo)致無(wú)法很直接地判斷 append 時(shí)是否創(chuàng)建了新數(shù)組。
如果一次添加多個(gè)元素,容量又會(huì)怎樣變化呢?試比較下面兩個(gè)例子:
那么,是不是說(shuō),當(dāng)向一個(gè)空切片中插入 2n-1 個(gè)元素時(shí),容量就會(huì)被設(shè)置為 2n 呢?我們來(lái)試試其他的數(shù)據(jù)類型。
可以看到,根據(jù)切片對(duì)應(yīng)數(shù)據(jù)類型的不同,容量增長(zhǎng)的方式也有很大的區(qū)別。相關(guān)的源碼包括: src/runtime/msize.go , src/runtime/mksizeclasses.go 等。
我們?cè)倏纯辞衅跏挤强盏那樾巍?/p>
可以看到,與剛剛向空切片添加5個(gè)int的情況一致,向有3個(gè)int的切片中添加2個(gè)int,容量增長(zhǎng)為6。
需要注意的是, append 對(duì)切片擴(kuò)容時(shí),如果容量超過(guò)了一定范圍,處理策略又會(huì)有所不同??梢钥纯聪旅孢@個(gè)例子。
具體為什么會(huì)是這樣的變化過(guò)程,還需要從 源碼 中尋找答案。下面是 src/runtime/slice.go 中的 growslice 函數(shù)中的核心部分。
GoLang中的切片擴(kuò)容機(jī)制,與切片的數(shù)據(jù)類型、原本切片的容量、所需要的容量都有關(guān)系,比較復(fù)雜。對(duì)于常見數(shù)據(jù)類型,在元素?cái)?shù)量較少時(shí),大致可以認(rèn)為擴(kuò)容是按照翻倍進(jìn)行的。但具體情況需要具體分析。
"切完不延切片平面整齊切下" 啥意思。。。 我一般使用切邊 是點(diǎn)級(jí)別切割 使用XYZ數(shù)值移動(dòng) 旋轉(zhuǎn) 或者捕捉 這樣確定方位是比較精準(zhǔn)的。