Go 中數(shù)組的長(zhǎng)度是不可改變的,而 Slice 解決的就是對(duì)不定長(zhǎng)數(shù)組的需求。他們的區(qū)別主要有兩點(diǎn)。
公司主營(yíng)業(yè)務(wù):網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶(hù)真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)建站是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶(hù)帶來(lái)驚喜。創(chuàng)新互聯(lián)建站推出安吉免費(fèi)做網(wǎng)站回饋大家。
數(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ǔ)言類(lèi)似,Go語(yǔ)言的數(shù)組也是一個(gè)元素類(lèi)型相同的定長(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ǔ)言的聲明,可以表明類(lèi)型,也可以不表明類(lèi)型,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)可見(jiàn),創(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]
可見(jiàn),切片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è)同類(lèi)型的其他值。如果追加的元素?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有切片slice類(lèi)型,python有列表和元組,這兩種語(yǔ)言都有切片操作。
但是它們的切片操作是完全不同的。
首先說(shuō)第一個(gè),go的切片,其成員是相同類(lèi)型的,python的列表和元組則不限制類(lèi)型。
兩種語(yǔ)言都有[a:b]這種切片操作,意義也類(lèi)似,但是go的a、b兩個(gè)參數(shù)不能是負(fù)數(shù),python可以是負(fù)數(shù),此時(shí)就相當(dāng)于從末尾往前數(shù)。
兩種語(yǔ)言都有[a:b:c]這種切片操作,意義卻是完全不同的。go的c,表示的是容量;而python的c表示的是步長(zhǎng)。
但是最大的不同,還是:
python的切片產(chǎn)生的是新的對(duì)象,對(duì)新對(duì)象的成員的操作不影響舊對(duì)象;go的切片產(chǎn)生的是舊對(duì)象一部分的引用,對(duì)其成員的操作會(huì)影響舊對(duì)象。
究其原因,還是底層實(shí)現(xiàn)的不同。
go的切片,底層是一個(gè)三元組,一個(gè)指針,一個(gè)長(zhǎng)度,一個(gè)容量。指針指向一塊連續(xù)的內(nèi)存,長(zhǎng)度是已有成員數(shù),容量是最大成員數(shù)。切片時(shí),一般并不會(huì)申請(qǐng)新的內(nèi)存,而是對(duì)原指針進(jìn)行移動(dòng),然后和新的長(zhǎng)度、容量組成一個(gè)切片類(lèi)型值返回。也就是說(shuō),go的切片操作通常會(huì)和生成該切片的切片共用內(nèi)存。
不僅是切片,字符串、數(shù)組的切片也是一樣的,通常會(huì)共用內(nèi)存。
當(dāng)然也有異常情況,那就是切片時(shí),提供的容量過(guò)大,此時(shí)會(huì)申請(qǐng)新內(nèi)存并拷貝;或者對(duì)切片append超出容量,也會(huì)如此。這時(shí),新的切片,才不會(huì)和老切片共享內(nèi)存。(如果你切片/創(chuàng)建時(shí)提供的容量小于長(zhǎng)度,會(huì)panic)
python的列表,其實(shí)是個(gè)指針數(shù)組。當(dāng)然在下層也會(huì)提供一些空位之類(lèi)的,但基本就是個(gè)數(shù)組。對(duì)它們切片,會(huì)創(chuàng)建新的數(shù)組,注意,是創(chuàng)建新的數(shù)組!python的列表可沒(méi)有容量的概念。
這其實(shí)也體現(xiàn)了腳本語(yǔ)言和編譯語(yǔ)言的不同。雖然兩個(gè)語(yǔ)言都有類(lèi)似的切片操作;但是python主要目標(biāo)是方便;go主要目標(biāo)卻是快速(并彌補(bǔ)丟棄指針運(yùn)算的缺陷)。 a
定義一個(gè)切片,然后讓切片去引用一個(gè)已經(jīng)創(chuàng)建好的數(shù)組。基本語(yǔ)法如下:
索引1:切片引用的起始元素位
索引2:切片只引用該元素位之前的元素
例程如下:
在該方法中,我們未指定容量cap,這里的值為5是系統(tǒng)定義的。
在方法一中,可以用arr數(shù)組名來(lái)操控?cái)?shù)組中的元素,也可以通過(guò)slice切片來(lái)操控?cái)?shù)組中的元素。切片是直接引用數(shù)組,數(shù)組是事先存在的,程序員是可見(jiàn)的。
通過(guò) make 來(lái)創(chuàng)建切片,基本語(yǔ)法如下:
make函數(shù)第三個(gè)參數(shù)cap即容量是可選的,如果一定要自己注明的話,要注意保證cap≥len。
用該方法可以 指定切片的大小(len)和容量(cap)
例程如下:
由于未賦值系統(tǒng)默認(rèn)將元素值置為0,即:
數(shù)值類(lèi)型數(shù)組:????默認(rèn)值為 0
字符串?dāng)?shù)組:? ? ? ?默認(rèn)值為 ""
bool數(shù)組:? ? ? ? ? ?默認(rèn)值為 false
在方法二中,通過(guò)make方式創(chuàng)建的切片對(duì)應(yīng)的數(shù)組是由make底層維護(hù),對(duì)外不可見(jiàn),即只能通過(guò)slice去訪問(wèn)各個(gè)元素。
定義一個(gè)切片,直接就指定具體數(shù)組,使用原理類(lèi)似于make的方式。
例程如下:
//從數(shù)組中獲取 切片
span style="color:#c0c0c0;" /spanspan style="color:#000080;font-weight:600;"var/spanspan style="color:#c0c0c0;" /spansliceArrayspan style="color:#c0c0c0;" /spanspan style="color:#000000;"[/spanspan style="color:#800080;"10/spanspan style="color:#000000;"]/spanspan style="color:#000080;"int/spanspan style="color:#c0c0c0;" /spanspan style="color:#000000;"=/spanspan style="color:#c0c0c0;" /spanspan style="color:#000000;"[/spanspan style="color:#800080;"10/spanspan style="color:#000000;"]/spanspan style="color:#000080;"int/spanspan style="color:#000000;"{/spanspan style="color:#800080;"0/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"1/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"2/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"3/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"4/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"5/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"6/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"7/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"8/spanspan style="color:#000000;",/spanspan style="color:#c0c0c0;" /spanspan style="color:#800080;"9/spanspan style="color:#000000;"}/span
//指定 begin index 和end Index
// begin index 和end index 都指定的情況 包括 begin index, 不包括end index index 從0開(kāi)始
var slice2 []int = sliceArray[5:9]
// slice3 和slice2 指向同一個(gè)底層的數(shù)組
var slice3 []int = sliceArray[:]
//輸出結(jié)果 [5 6 7 8] 4 5
fmt.Println(slice2, len(slice2), cap(slice2))
//測(cè)試添加元素 and 擴(kuò)容之后的數(shù)組操作情況
//通過(guò)切片直接操作數(shù)組的信息
slice2[0] = 100
sliceArray[6] = 66
//多個(gè)切片 操作底層數(shù)組
slice3[7] = 77
//輸出結(jié)果 [100 66 77 8] 4 5 三個(gè)賦值 都影響了切片的底層數(shù)組
fmt.Println(slice2, len(slice2), cap(slice2))
//輸出結(jié)果 [0 1 2 3 4 100 66 77 8 9] 10 10
fmt.Println(slice3, len(slice3), cap(slice3))
//輸出結(jié)果 [0 1 2 3 4 100 66 77 8 9] 三個(gè)賦值 都影響了 原始數(shù)組
fmt.Println("sliceArray:", sliceArray)
//擴(kuò)容
// 操作的還是 sliceArray
slice2 = append(slice2, 99)
//輸出結(jié)果 [100 66 77 8 99] 5 5
fmt.Println(slice2, len(slice2), cap(slice2))
// 輸出結(jié)果 sliceArray: [0 1 2 3 4 100 66 77 8 99]
fmt.Println("sliceArray:", sliceArray)
slice2 = append(slice2, 1000)
//輸出結(jié)果 [100 66 77 8 99 1000] 6 10 此處切片已經(jīng)擴(kuò)容(兩倍擴(kuò)容), 并保留了原始的內(nèi)容
fmt.Println(slice2, len(slice2), cap(slice2))
// 輸出結(jié)果 sliceArray: [0 1 2 3 4 100 66 77 8 99] 原來(lái)的數(shù)組不再受到影響了
fmt.Println("sliceArray:", sliceArray)
//通過(guò)index操作 元素 判斷擴(kuò)容后的底層數(shù)組是否是部分會(huì)在原始的 數(shù)組上面
slice2[1] = 999
//輸出結(jié)果 [100 999 77 8 99 1000] 6 10
fmt.Println(slice2, len(slice2), cap(slice2))
// 輸出結(jié)果 sliceArray: [0 1 2 3 4 100 66 77 8 99] 說(shuō)明是 重新找了一塊內(nèi)存, 和以前的數(shù)組完全沒(méi)有關(guān)系
fmt.Println("sliceArray:", sliceArray)
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ū)分大小寫(xiě)的。
二分搜索算法
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)字符串相同的元素索引