2016-11-22 50 views
1

這是我在Golang第一天,當我嘗試它的切片操作,如append(),有一兩件事讓我很困惑:爲什麼Golang的切片操作這麼複雜

package main 

import "fmt" 

func main() { 
    s := []int{2, 3, 5, 7, 11, 13} 
    a:= s[2:4]; 
    a = append(a, 1000, 1001); 
    a[1] = 100; 
    printSlice("a:", a) 
    printSlice("s:", s) 
} 

func printSlice(title string, s []int) { 
    fmt.Printf("%s len=%d cap=%d %v\n", title, len(s), cap(s), s) 
} 

當我附上只有兩個數字到a,如:

a = append(a, 1000, 1001); 

...結果是:

a: len=4 cap=4 [5 100 1000 1001] 
s: len=6 cap=6 [2 3 5 100 1000 1001] 

我認爲,其中的a顯示爲參考s

但是,當我改變的代碼行:

a = append(a, 1000, 1001, 1002); 

...結果就變成:

a: len=5 cap=8 [5 100 1000 1001 1002] 
s: len=6 cap=6 [2 3 5 7 11 13] 

其中,我認爲,a已重新分配的內存另一段,來保存整個事物,並將對s的引用分開。

這太不一致了,讓我感到非常困惑,有時候很容易出現這個錯誤(例如當你有一個隨機數的值附加)。

爲什麼Golang是這樣設計的?如果我只是想要像JavaScript的slicepush這樣的操作,那怎麼能夠避免呢?

+2

我認爲這個想法是,你只是不'追加到視圖共享數據。如果你想採取子碟並追加它,你可以複製一份。 – user2357112

+7

如果您不理解切片背後的簡單語義,它只會顯得不一致。如果你不確定發生了什麼,你可以閱讀很多資源:[Go Slices:usage and internals](https://blog.golang.org/go-slices-usage-and-internals),[A遊覽之旅:切片](https://tour.golang.org/moretypes/7),[切片類型](https://golang.org/ref/spec#Slice_types)和[附加任何複製切片](https://golang.org/ref/spec#Appending_and_copying_slices),[Go By Example:Slices](https://gobyexample.com/slices)和[Slice Tricks](https://github.com/) golang/go/wiki/SliceTricks) – JimB

+0

@ user2357112謝謝,現在我很清楚。 – Kuan

回答

5

這是一個與Go如何實現切片有關的問題。

slice's struct樣子:

type slice struct { 
    array unsafe.Pointer 
    len int 
    cap int 
} 

因此,切片的長度和容量。如果您試圖將條目附加到切片以使其超出當前容量,則會在下面創建一個新數組以保存新數據,但由於以前的子切片仍然可能指向較舊的數組,因此它保持不變,直到沒有更多的參考資料留給它。


現在讓我們說我們有一個切片A[1, 2, 3, 4, 5, 6]和子切片B指向持續在A 3項:[4,5,6]。

[1, 2, 3, 4, 5, 6] 
^  ^
|  | 
|  B------ 
| 
A--------------- 

現在,如果我們追加一個項目B然後從預期的行爲應該更新A以及因此一個新數組歸因於被創建。如果子切片的大小與實際數組相比較小(用於追加1個項目從原始數組複製1000個項目),這可能是低效的。

爲了保持一致性,指向舊數組的所有其他引用(子)必須更新爲指向新數組中的適當位置,這意味着我們必須在切片中存儲其他信息,例如開始索引。如果我們有子切口的子切口,這可能會變得棘手。

因此當前的實現是有意義的。


這裏推薦的方法是複製子切片而不是直接對它進行操作以防止出現此類問題。有一個副本的另一個好處是,如果原始切片是巨大的,並且沒有引用,那麼它可以被垃圾收集,但是如果它存在子切片,則原始數組將保留它的內存直到子切片仍然引用到它。

+0

'如果我們追加一個項目到B然後從你預期的行爲,它應該更新A,因此,一個新的數組將被創建,因爲這個。「不,這不是我的預期行爲,我的預期行爲應該只是創建一個新的數組,並複製一切B指的是新的數組並添加新項目。答應永遠不會碰(因爲無論如何,會有一個新的陣列創建)。我不得不說這個實現很糟糕 – Kuan

相關問題