Goでスライスをどのようにクリアしますか?


125

Goでスライスをクリアする適切な方法は何ですか?

これが私がgoフォーラムで見つけたものです:

// 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))
}

これは正しいです?

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

例は、bytesパッケージの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
簡単なテスト:play.golang.org/p/6Z-qDQtpbgは機能することを示唆しているようです(容量は変更されませんが、長さが切り捨てられます)
Jason Sperske

回答:


120

それはすべて、「明確」の定義が何であるかに依存します。有効なものの1つは確かに次のとおりです。

slice = slice[:0]

しかし、落とし穴があります。スライス要素がタイプTの場合:

var slice []T

その後、強制len(slice)ゼロであると、上記の「トリック」で、 しないの任意の要素を作ります

slice[:cap(slice)]

ガベージコレクションの対象。これは、いくつかのシナリオで最適なアプローチである可能性があります。しかし、それは「メモリリーク」の原因である可能性もあります。メモリは使用されていませんが、到達可能であり(「スライス」のスライス後)、ガベージで「収集可能」ではありません。


1
面白い。基本的な容量を変更せずに、スライスの基本的な配列からすべての要素を削除する他の方法はありますか?
Chris Weber

3
@ChrisWeber:配下の配列を反復処理し、すべての要素を新しい値に設定する
newacct 2013年

2
@jnml、私はスライス(および配下の配列ストレージ)を再利用したいので、常に新しい配列(配列)を割り当てていません。私は質問を編集して、標準ライブラリのコード例を明確にし、表示しました。
Chris Weber

1
Goは初めてです。これが最適なアプローチになる理由について詳しく説明していただけますか?前もって感謝します。
satoru 2016年

スライスサイズのリセットによってメモリリークが発生してもよろしいですか?再現できません
Tommaso Barbugli

197

スライスをに設定することnilは、スライスをクリアする最良の方法です。 nilgoのスライスは完全に適切に動作し、スライスをに設定するnilと、基になるメモリがガベージコレクターに解放されます。

遊び場を見る

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)
}

プリント

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

2つのスライスが同じ基礎となるメモリを指すように、スライスにエイリアスを簡単に設定できることに注意してください。への設定nilはそのエイリアシングを削除します。

ただし、この方法では容量がゼロに変更されます。


ニックレスポンスに感謝します。私のアップデートをご覧ください。スライスをクリアして再利用します。だから私はちょうどそれを再び割り当てる必要があるので、私は必ずしもGCに解放された基礎となるメモリを望んでいません。
Chris Weber

それは私が検索したものです!)
Timur Fayzrakhmanov '19

5
「囲碁でスライスをどのようにクリアしますか?」というタイトルに基づいています。これははるかに安全な答えであり、受け入れられるべきものです。完璧な答えは、最初に受け入れられた答えとこれを組み合わせたものになるでしょう。
Shadoninja

1
appendnilスライスへのing は常にGoで機能していますか?
alediaferia 2016年

@alediaferia以来1.0は確かに行く。
Nick Craig-Wood

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)
}

そのコードをそのまま実行すると、「meow」変数と「meow2」変数の両方に同じメモリアドレスが同じとして表示されます。

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

これは、構造体がガベージコレクションされていることを確認します。奇妙なことに、コメントされた印刷行のコメントを外すと、ニャーのメモリアドレスが異なります。

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

これは、印刷が何らかの方法(?)で延期されたためかもしれないと思いますが、いくつかのメモリ管理動作の興味深い説明と、もう1つの投票:

[]MyStruct = nil

素晴らしい詳細な例。ありがとう!
Dolanor 2017

2
これは、meo1とmeow2のメモリアドレスが同じであることを示していませ0x1030e0c0ん。等しくありません0x1030e0f0(前者はで終わりc0、後者はで終わりますf0)。
カーボカチオン

ここで@carbocationに同意しました。これらのメモリアドレスは同じではありません。私はここで何が起こっているのかをよりよく説明できるとは主張していませんが、これは私にとって証拠にはなりません。meow2各実行のアドレスで同じ8バイトの不一致が見られます...
rbrtl
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.