2013-06-06 27 views
75

Goでスライスを消去する適切な方法は何ですか?ここでGoでスライスをクリアするにはどうすればよいですか?

は、私がgo forumsで見つけたものです:

// test.go 
package main 

import (
    "fmt" 
) 

func main() { 
    letters := []string{"a", "b", "c", "d"} 
    fmt.Println(cap(letters)) 
    fmt.Println(len(letters)) 
    // clear the slice 
    letters = letters[:0] 
    fmt.Println(cap(letters)) 
    fmt.Println(len(letters)) 
} 

は、この正しいですか?

明確にするために、バッファをクリアして再利用できるようにします。

例は、バイトパッケージのBuffer.Truncate関数です。

ResetはTruncate(0)だけを呼び出します。だから、この場合にはライン70を評価するだろうと思わ: b.buf = b.buf [0:0]

http://golang.org/src/pkg/bytes/buffer.go

// Truncate discards all but the first n unread bytes from the buffer. 
60 // It panics if n is negative or greater than the length of the buffer. 
61 func (b *Buffer) Truncate(n int) { 
62  b.lastRead = opInvalid 
63  switch { 
64  case n < 0 || n > b.Len(): 
65   panic("bytes.Buffer: truncation out of range") 
66  case n == 0: 
67   // Reuse buffer space. 
68   b.off = 0 
69  } 
70  b.buf = b.buf[0 : b.off+n] 
71 } 
72 
73 // Reset resets the buffer so it has no content. 
74 // b.Reset() is the same as b.Truncate(0). 
75 func (b *Buffer) Reset() { b.Truncate(0) } 
+1

のクイックテスト:http://play.golang.org/p/6Z-qDQtpbgはそれが動作することを示唆しているようだ(容量を変更するが、それはありません長さを切り捨てます) –

答えて

72

これは、すべての 'クリア' のあなたの定義が何であるかに依存します。有効なものの1つは確かです:

slice = slice[:0] 

しかし、キャッチがあります。スライス要素の型がTである場合:

var slice []T 

は、上記の「トリック」で、ゼロであるlen(slice)を強制、

slice[:cap(slice)] 
ガベージコレクションの対象

のいずれかの要素がありません。これは、いくつかのシナリオでは最適なアプローチかもしれません。しかし、メモリが使用されていませんが、潜在的に到達可能(スライスの再スライス後)なので、ゴミは回収できません。

+0

興味深い。基礎となる容量を変更せずにスライスの基本アレイからすべての要素を削除する他の方法はありますか? –

+3

@ChrisWeber:基本的な配列を繰り返し処理し、すべての要素を新しい値 – newacct

+0

@newacctに設定してください。ありがとう、私はそれを推測していました。 –

122

スライスをnilに設定すると、スライスを消去する最適な方法です。 nilのスライスは完全に正常に動作し、スライスをnilに設定すると、下位のメモリがガベージコレクタに解放されます。

See playground

package main 

import (
    "fmt" 
) 

func dump(letters []string) { 
    fmt.Println("letters = ", letters) 
    fmt.Println(cap(letters)) 
    fmt.Println(len(letters)) 
    for i := range letters { 
     fmt.Println(i, letters[i]) 
    } 
} 

func main() { 
    letters := []string{"a", "b", "c", "d"} 
    dump(letters) 
    // clear the slice 
    letters = nil 
    dump(letters) 
    // add stuff back to it 
    letters = append(letters, "e") 
    dump(letters) 
} 

プリント2つのスライスは、同じ基本メモリを指すようにスライスが容易にエイリアスすることができる

letters = [a b c d] 
4 
4 
0 a 
1 b 
2 c 
3 d 
letters = [] 
0 
0 
letters = [e] 
1 
1 
0 e 

注。 nilに設定すると、そのエイリアシングが削除されます。

この方法は、容量をゼロに変更します。

+0

ニックの応答のおかげで。私の更新を見てください。私は再利用のためにスライスをクリアしています。だから、必ずしも元のメモリをGCにリリースして、それをもう一度割り当てる必要はありません。 –

+0

これは私が検索したものです!) –

+3

タイトルに基づいて "Goでスライスをクリアする方法は?"これははるかに安全な答えであり、受け入れられたものでなければなりません。完璧な答えは、もともと受け入れられた回答とこれを組み合わせて、人々が自分で決定できるようにすることです。 – Shadoninja

4

私はこの問題を私自身の目的のために少し調べていました。私は構造体(いくつかのポインタを含む)のスライスを持っていた、私はそれが正しいことを確認したいと思った。このスレッドで終わり、結果を共有したいと思っていました。 https://play.golang.org/p/9i4gPx3lnY

これに試用版:

package main 

import "fmt" 

type Blah struct { 
    babyKitten int 
    kittenSays *string 
} 

func main() { 
    meow := "meow" 
    Blahs := []Blah{} 
    fmt.Printf("Blahs: %v\n", Blahs) 
    Blahs = append(Blahs, Blah{1, &meow}) 
    fmt.Printf("Blahs: %v\n", Blahs) 
    Blahs = append(Blahs, Blah{2, &meow}) 
    fmt.Printf("Blahs: %v\n", Blahs) 
    //fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays) 
    Blahs = nil 
    meow2 := "nyan" 
    fmt.Printf("Blahs: %v\n", Blahs) 
    Blahs = append(Blahs, Blah{1, &meow2}) 
    fmt.Printf("Blahs: %v\n", Blahs) 
    fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays) 
} 

そのコードを実行しているとして、ある「ニャー」の両方に同じメモリアドレスが表示されますし、練習するには

、私は少し行く遊び場をしました同じものとして「meow2」変数:

Blahs: [] 
Blahs: [{1 0x1030e0c0}] 
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}] 
Blahs: [] 
Blahs: [{1 0x1030e0f0}] 
kittenSays: nyan 

私は構造体がガベージコレクトされていることを確認したと思います。奇妙なことに、コメント印字行のコメントを解除し、meowsのための異なるメモリアドレスを生成します:(?)

Blahs: [] 
Blahs: [{1 0x1030e0c0}] 
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}] 
kittenSays: meow 
Blahs: [] 
Blahs: [{1 0x1030e0f8}] 
kittenSays: nyan 

私は、これは何らかの方法で延期されて印刷が原因かもしれないと思うが、いくつかのメモリMGMTの興味深い実例動作、およびのための1件の以上の投票:

[]MyStruct = nil 
+0

良い詳細な例。ありがとう! – Dolanor