數組和切片

對比學習數組和切片

數組的長度是固定的,切片是可變長的。

數組的類型字面量中必須有元素的類型和長度。數組的長度在聲明的時候必須給定,並且之後不會再改變,數組的長度是其類型的一部分。比如[1]string[2]string就是2個不同的數組類型。

切片的類型字面量中只有元素的類型,而沒有長度。切片的長度可以自動地隨着其中元素數量的增長而增長,但不會隨着元素數量的減少而減小。

可以把切片看作是對數組的一層簡單封裝,因爲在每個切片額底層數據結構中,一定會包含一個數組。數組可以叫做切片的底層數組,而切片可以看做是對數組的某個連續片段的引用。

正因爲“切片可以看作是數組的某個連續片段的引用”,Go語言的切片類型屬於引用類型。同屬於引用類型的還有字典類型,通道類型,函數類型等;而Go語言的數組類型則屬於值類型,同屬於值類型的還有基礎數據類型以及結構體類型。

Go語言裏不存在像Java等編程語言中令人困惑的“傳值或傳引用”問題。在Go語言中,我們判斷所謂的“傳值”或者“傳引用”只要看傳遞的值的類型就好了,如果傳遞的值的類型是值類型,那麼就是“傳值”,如果傳遞的值的類型是引用類型的,那麼就是“傳引用”。從傳遞成本的角度講,引用類型的值往往要比值類型的值低很多。

可以在數組和切片之上應用“索引表達式”,得到的是某個元素。 也可以在數組和切片上應用“切片表達式”,得到的是一個新的切片。

調用內建函數len,可以得到數組和切片的長度。數組的容量永遠等於其長度,都是不可變的。 調用內建函數cap,可以得到數組和切片的容量。切片的容量是可變的,變化也是有規律可循的。

怎樣正確估算切片的長度和容量?

內建函數make可以創建切片。 make([]int,5,8)第一個參數[]int指明切片的類型,第二個參數5指明切片的長度,第三個參數8指明切片的容量。

切片的容量是什麼意思呢? 還記得上面說的嗎:數組是切片的底層數據結構的一部分。切片的容量實際上代表了它的底層數組的長度。 在切片s1上應用切片表達式,得到的新的切片s2的底層數組和s1的底層數組是一樣的。

回答問題:怎樣估算切片容量的增長?

一旦一個切片無法容納更多的元素,Go語言就會想辦法擴容。但是它並不會改變原來的切片,而是會生成一個容量更大的切片,然後把原有的元素和新元素一併拷貝到新的切片總。在一般情況下,可以簡單地認爲新切片的容量(簡稱新容量)將會是原切片容量(簡稱原容量)的2倍。

但是當原切片的長度(原長度)大於或等於1024時,Go語言將會以原容量的1.25倍作爲新容量的基準(新容量基準)。新容量基準將會被調整(不斷地與1.25相乘),直到結果不小於原長度與要追加的元素數量之和(簡稱新長度)。最終,新容量往往會比新長度大一些,當然,相等也是可能的。

另外,如果我們一次追加的元素過多,以至於新長度比原容量的2倍還要大,那麼新容量就會以新長度爲基準。與前面的情況一樣,最終的容量在很多時候都要比新容量基準更大一些。

問題2:切片的底層數組什麼時候會被替換?

確切的說,一個切片的底層數組永遠不會被替換。爲什麼?雖然在擴容的時候,Go語言一定會生成新的底層數組,但是它同時也生成了新的切片。它是把新的切片作爲了新底層數組的窗口,而沒有對原切片及其底層數組做任何改動。

請記住,在無需擴容的時候,append函數返回的是指向原底層數組的新切片,而在需要擴容的時候,append函數返回的是指向新底層數組的新切片。所以,嚴格來講,“擴容”這個詞用在這裏雖然形象但是不合適。

只要新長度不會超過原切片的原容量,那麼使用append函數對其追加元素的時候就不會引起擴容。這隻會使緊鄰切片窗口右邊(底層數組中的)元素被新的元素替換掉。

問題3:如果有多個切片指向了同一個底層數組,應該注意什麼?

初始時兩個切片引用同一個底層數組,在後續操作中對某個切片的操作超出底層數組的容量時,這兩個切片引用的就不是同一個數組了

s1 :=[]int{1,2,3,4} s2 := s1[0:4] 就像這樣,這樣的話改變s2會影響s1,如何消除這種影響呢

可以用copy函數,或者自己深拷貝。

問題4:怎樣援用“擴容”的思想對切片進行“縮容”?

切片縮容之後還是會引用底層的原數組,這有時候會造成大量縮容之後的多餘內容沒有被垃圾回收。可以使用新建一個數組然後copy的方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章