2017-07-31 58 views
5

在Golang中,我們可以使用內建的make()函數來創建具有給定初始長度和容量的切片。設置切片容量的要點是什麼?

考慮以下行,片的長度被設置爲1,其容量3:

func main() { 
    var slice = make([]int, 1, 3) 
    slice[0] = 1 
    slice = append(slice, 6, 0, 2, 4, 3, 1) 
    fmt.Println(slice) 
} 

我驚奇地看到,這個程序打印:

[1 6 0 2 4 3 1]

這讓我想知道 - 如果append()可以簡單地吹過它,最初定義切片的容量是什麼點?設置足夠大的容量是否有性能提升?

回答

10

切片實際上只是管理底層陣列的一種奇特方式。它會自動跟蹤大小,並根據需要重新分配新空間。

當您添加到切片中時,每次超過其當前容量時其容量會翻倍。它必須複製所有元素才能做到這一點。如果您在開始之前知道它會有多大,您可以預先抓住它,避免一些複製操作和內存分配。

當你make提供能力片,設置謂初始容量,沒有任何形式的限制

查看this blog post on slices查看切片的一些有趣的內部細節。

+2

在某一點上,它停止加倍和在25倍%的增量開始增加。我認爲這發生在1024個元素之後。 –

+0

這不僅僅是複製操作需要時間,它也是alloc。 – Adrian

4

A slice是一個簡單的array美妙的抽象。你可以得到各種不錯的功能,但是它的核心內容是array。 (我以相反的順序解釋以下原因)。因此,如果/當您指定一個capacity3,深度下來時,將在內存中分配一個長度爲3的數組,這樣您就可以在不需要重新分配內存的情況下分配內存。該屬性在make命令中是可選的,但請注意,slice總是會有capacity,不管您是否選擇指定一個。如果您指定length(它總是存在),則slice可以索引到該長度。 capacity的其餘部分被隱藏在幕後,所以當使用append時,它不必分配全新的陣列

下面是一個更好地解釋機制的例子。

s := make([]int, 1, 3)

底層array將與int零值的3(這是0)被分配:

[0,0,0]

然而,length設置爲1,所以切片本身只會打印[0],如果您嘗試索引第二個或第三個值,它將panic作爲slice的機制不允許。如果您對其使用s = append(s, 1),則會發現它實際上已創建爲包含zero的值,最大值爲length,最後將以[0,1]結尾。在這一點上,您可以在完整的基礎array填充之前再次登錄append,而另一個append將強制它分配一個新的值,並將所有值複製到一倍的容量上。這實際上是一個相當昂貴的操作。

因此您的問題的簡短答案是,預分配capacity可用於大大提高您的代碼的效率。尤其如此,如果slice要麼變得非常大,要麼包含複雜的structs(或兩者),因爲structzero值實際上是其每個fieldszero值。這不是因爲它會避免分配這些值,因爲它無論如何,但是因爲append將不得不重新分配新的array s這些零值,每次它需要調整底層數組的大小。

短操場例如:https://play.golang.org/p/LGAYVlw-jr