GOでスライスからアイテムを削除する正しい方法は何ですか?
また、スライスを再初期化する正しい方法は何ですか?
スライスの性質を誤解していると思います。スライスはArrayList
Java の のようなものです。通常のアレイに支えられており、オンデマンドで拡大/縮小します。スライスでの操作は、 で期待されるものと同じパフォーマンス特性を持っていますArrayList
。
LinkedList
スライスが同等であれば、あなたの質問はより理にかなっています。そのためには、Package listを調べてください。
それにもかかわらず、これを行う方法は次のとおりです。ほとんどはSliceTricksから直接来ていますが、SO ではリンクを参照せず、ここで答えを提供することをお勧めします。
これは、順序を気にしなければ、どのプログラミング言語でも O(1) 時間で実行できることです。順序を気にする場合、これは機能しません。
アイデアは、削除するアイテムをスライスの最後のアイテムで上書きしてから、スライスのサイズを1つ減らすことです。
arr := []string{ "allo", "hello", "bye", "chao" }
// delete "bye"
deleteIdx := 2
lastIdx := len(arr) - 1
// arr = { "allo", "hello", "chao", "chao" }
arr[deleteIdx] = arr[lastIdx]
// arr = { "allo", "hello", "chao" } ... "chao"
arr = arr[:lastIdx - 1]
1 つのステップでそれを行うことができます(SliceTricks) :
arr[deleteIdx], arr = arr[len(arr)-1], arr[:len(arr) - 1]
nil
ただし、SliceTricks の記事で述べたように、スライスの背後にあるバッキング配列がまだそれらへの参照を保持しているため、ガベージ コレクションを行わないと一部のタイプの値はガベージ コレクションされません。解決策はnil
、操作を行っている間です。
arr[len(arr)-1], arr[deleteIdx], arr = nil, arr[len(arr)-1], arr[:len(arr)-1]
// ^ Setting the deleted index to nil ^
もちろん、順序を維持することを気にしない場合は、これですべてです。気にする場合は、deleteIdx
最初からやり直した後、すべてをコピーする必要がありますdeleteIdx
。これは O(n) です。これを行っていることに気付いた場合は、ニーズに合ったより良いデータ構造がないか考えてみてください。
// Copy everything from [deleteIdx+1 .. n) onto [deleteIdx .. )
copy(arr[deleteIdx:], arr[deleteIdx+1:])
// arr[n - 1] and arr[n] have the same value (n = len(arr) - 1)
arr[len(arr)-1] = nil
// re-slice to reference only the n-1 elements
arr = arr[:len(arr)-1]
すべてのアイテムを再スライスすることで、スライスを再初期化できます
// Keep everything from [0 .. 0), which means keep nothing
arr = arr[:0]
ただし、これを行うには問題があります。前述のように、スライスのバッキング配列は、スライスにあった元のアイテムを引き続き参照します。代わりにすべきことは、新しいスライスを作成し、これをガベージ コレクトすることです。