golang 陣列和切片
陣列型別的值(以下簡稱陣列)的長度是固定的,而切片型別的值(以下簡稱切片)是可變長的。
陣列的長度在宣告它的時候就必須給定,並且在之後不會再改變。可以說,陣列的長度是其型別的一部分。
golang陣列的四種宣告方法
第一種
// var <陣列名稱> [<陣列長度>]<陣列元素> var arra [2]int arr[0]=1 arrp1[=2
第二種
// var <陣列名稱> = [<陣列長度>]<陣列元素>{元素1,元素2,...} var arr = [2]int{1,2} //或者 arr := [2]int{1,2}
第三種
// var <陣列名稱>[<陣列長度>]<陣列元素>=[...]<元素型別>{元素1,元素2,...} var arr =[...]int{1,2} //或者 arr :=[...]int {1,2}
第四種
// var <陣列名稱>[<陣列長度>]<陣列元素>=[...]<元素型別>{索引1:元素1,索引2:元素2,...} var arr = [...]int{1:1,0:2} // 或者 arr := [...]int{1:1,0:2}
切片的長度可以自動地隨著其中元素數量的增長而增長,但不會隨著元素數量的減少而減少。
golang切片的3種宣告方法
①定義一個切片,然後讓切片去引用一個已經建立好的陣列
var arr [5]int = [...]int{1,2,3,4,5} var slice = arr [1:3]
②通過make來建立切片
// var 切片名 []type = make([], len, [cap]);引數說明:type是資料型別、len是大小、cap是切片容量(容量必須>=長度) 通過make方式建立切片可以指定切片大小和容量 如果沒有給切片的各個元素賦值,那麼就會使用預設值(int、float=>0, strint=>"", bool=>false) 榮國make方式建立的切片對應的陣列是由make底層維護,對外不可見,也就是隻能通過slice訪問各個元素 var slice []float64 = make([]float64, 5, 10)
③定義一個切片,直接就指定具體陣列,使用原理類似於make的方式
var slice []string = []string{"zhangsan", "lisi", "wangwu"}
我們其實可以把切片看做是對陣列的一層簡單的封裝,因為在每個切片的底層資料結構中,一定會包含一個數組。後者可以被叫做前者的底層陣列,而前者也可以被看作是對後者的某個連續片段的引用。
也正因為如此,Go語言的切片型別屬於引用型別,同屬引用型別的還有後面會講到的字典型別、通道型別、函式型別等;而Go語言的陣列型別則屬於值型別,同屬值型別的有基礎資料型別以及結構體型別。
注意,Go語言裡不存在像java等程式語言中那種令人困惑的“傳值或傳引用”問題。在Go語言中,我們判斷所謂的“傳值”或者“傳引用”只要看北傳遞的值的型別就好了。如果傳遞的值是引用型別的,那麼就是“傳引用”。如果傳遞的值是值型別的,那麼就是“傳值”。從傳遞成本的角度講,引用型別的值往往要比值型別的值低很多。
怎樣正確估算切片的長度和容量?
package main import "fmt" func main(){ // 示例1 s1 :=make([]int,5) fmt.println("The length of s1: %d\n",len(s1)) fmt.println("The capacity of s1:%d\n",cap(s1)) fmt.println("The value of s1: %d\n",s1) s2 := make([]int,5,8) fmt.println("The length of s2:%d\n",len(s2)) fmt.println("The capacity of s2:%d\n",cap(s2)) fmt.println("The value of s2: %d\n",s2) }
首先,內建函式make 聲明瞭一個[]int型別的變數s1 。我傳給make函式的第二個引數是5,從而指明瞭該切片的長度。再用幾乎相同的方式聲明瞭切片s2 ,只不過多傳了一個引數8以指明該切片的容量。
切片s1和s2的容量分別是5和8。
原因:當我們用make函式初始化切片時,如果不指名其容量,那麼它就會和長度一致。如果在初始化時指明瞭容量,那麼切片的實際容量也就是它了。這也正是s2的容量是8的原因。
s3: = []int{1,2,3,4,5,6,7,8} s4:= s3[3:6]
s4的長度為3,容量為5
原因:由於s4是通過在s3上施加切片操作得來的,所以s3的底層陣列就是s4的底層陣列。又因為,在底層陣列不變的情況下,切片代表的視窗可以向右擴充套件,直至其底層陣列的末尾。所以,s4的容量就是其底層陣列的長度8減去上述切片表示式中的那個起始索引3,即5。
注意:切片帶邊的視窗是無法向左擴充套件的也就是說,我們永遠無法透過s4看到s4左邊的那3個元素。
最後,隨便提一下把切片的視窗向右擴充套件到最大的方法。對於s4來說,切片表示式s4[0:cap(s4)]就可以做到。該表示式的結果(即一個新的切片)會是[]int{4,5,6,7,8},其長度和容量都是5。
知識擴充套件
1.切片如何擴容?
一旦一個切片無法容納更多的元素,Go於洋就會想辦法擴容。但它並不會改變原來的切片,而是生成一個容量更大的切片,然後將把原有元素和新元素一併拷貝到新切片中。
在一般的情況下,你可以簡單地認為新切片的容量將是原切片容量的2倍。
但是,當原切片的長度大於或等於1024時,Go語言會將以原容量的1.25倍作為新容量的基準。
新容量基準會被調整(不斷與1.25相乘),直到結果不小於原長度與要追加的元素數量之和(以下簡稱新長度)。最終,新容量往往會比新長度要大一些,當然,相等也是可能得。
2.切片的底層陣列什麼時候會被替換?
確切地說,一個切片的底層陣列永遠不會被替換。雖然在擴容的時候Go語言一定會生成新的底層陣列,但是它同時生成了新的切片。它是把新的切片作為了新底層陣列的視窗,而沒有對原切片及其底層陣列做任何改動。
在無須擴容時,append 函式返回的是指向原底層陣列的切片,而在需要擴容時,append函式返回的是指向新底層陣列的新切片。
所以,嚴格來說,“擴容”這個詞用來這裡雖然形象但並不適合。
順便說一下,只要新長度不會超過切片的原容量,那麼使用append 函式對其追加元素的時候就不會引起擴容。這隻會使緊鄰切片視窗右邊的(底層陣列中的)元素被替換成新的元素。