删除切片元素并重新初始化切片。

huangapple go评论86阅读模式
英文:

Remove slice element and reinitialize slice

问题

从切片中删除元素的正确方法是使用切片的索引和切片操作符。例如,要从切片中删除索引为i的元素,可以使用以下代码:

slice = append(slice[:i], slice[i+1:]...)

这将创建一个新的切片,其中包含原始切片中除了索引为i的元素之外的所有元素。

要重新初始化一个切片并完全清空它,可以使用以下代码:

slice = nil

这将将切片设置为nil,表示它不包含任何元素。但是,切片的容量仍然保持不变,以便可以继续向其中添加元素。

英文:

What is the correct way to remove an item from a slice in GO?

Also, what is the correct way to reinitialize a slice i.e. completely empty it but still keep it?

答案1

得分: 3

我相信你对切片的本质有一些误解。切片类似于Java中的ArrayList,它由一个普通数组支持,并在需要时动态增长/缩小。对切片的操作具有与ArrayList相似的性能特征。

如果切片是LinkedList的等价物,你的问题会更有意义。关于这一点,你可以查阅Package list

不过,我还是会告诉你如何做。大部分内容来自SliceTricks,但我认为在这里提供答案而不是引用链接是一个好的做法。

从切片中删除元素的方法

如果你不关心顺序,这是你可以在任何编程语言中以O(1)的时间复杂度完成的操作。如果你关心顺序,这种方法就不适用。

思路是用切片中的最后一个元素覆盖你想要删除的元素,然后将切片的大小减一。

arr := []string{"allo", "hello", "bye", "chao"}

// 删除"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]

你也可以使用一步完成(SliceTricks):

arr[deleteIdx], arr = arr[len(arr)-1], arr[:len(arr)-1]

然而,正如在SliceTricks文章中提到的,如果你不将某些类型的值设为nil,它们将不会被垃圾回收,因为切片背后的底层数组仍然引用它们。解决办法是在操作时将它们设为nil

arr[len(arr)-1], arr[deleteIdx], arr = nil, arr[len(arr)-1], arr[:len(arr)-1]
//  ^ 将被删除的索引设为nil ^

当然,这一切都是在你不关心保持顺序的情况下。如果你关心顺序,你需要将deleteIdx之后的所有元素复制到从deleteIdx开始的位置,这是O(n)的操作。如果你发现自己经常这样做,考虑是否有更适合你需求的数据结构。

// 将[deleteIdx+1 .. n)的所有元素复制到[deleteIdx .. )上
copy(arr[deleteIdx:], arr[deleteIdx+1:])
// arr[n - 1]和arr[n]具有相同的值(n = len(arr) - 1)
arr[len(arr)-1] = nil
// 重新切片,只引用前n-1个元素
arr = arr[:len(arr)-1]

重新初始化切片,即完全清空但保留切片

你可以通过重新切片将切片的所有元素都切出来来重新初始化切片。

// 保留[0 .. 0)之间的所有元素,也就是不保留任何元素
arr = arr[:0]

但是这样做存在一个问题:如上所述,切片的底层数组仍然引用原来在切片中的元素。相反,你应该创建一个新的切片,并让旧的切片被垃圾回收。

英文:

I believe you are misunderstanding the nature of a slice. A slice is like an ArrayList in Java. It is backed by a regular array and grows/shrinks on demand. Operations on a slice have the same performance characteristic as those you would expect on an ArrayList.

Your question(s) would make more sense if slices were the LinkedList equivalent. For that, look up Package list.

Nevertheless, here's how to do this. Most comes directly from SliceTricks, but I think it's good practice on SO to not refer to links and provide the answer right here.

Way to remove an item from a slice

This is something that you can do in any programming language, in O(1) time, if you don't care about order. If you care about order, this is not going to work.

The idea is to overwrite the item you want to remove with the last item in the slice, then reduce the size of the slice by one.

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]

You can do that in a single step (SliceTricks):

arr[deleteIdx], arr = arr[len(arr)-1], arr[:len(arr) - 1]

However, like mentionned in the SliceTricks article, some type of values will not be garbage collected if you don't nil them, as the backing array behind the slice still holds a reference to them. The solution is to nil them while doing the operation.

arr[len(arr)-1], arr[deleteIdx], arr = nil, arr[len(arr)-1], arr[:len(arr)-1]
//  ^ Setting the deleted index to nil ^

This is all, of course, if you don't care about preserving order. If you do care, you will need to copy everything after deleteIdx starting over deleteIdx, which is O(n). If you find yourself doing this, think if there isn't a better datastructure for your needs.

// 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]

Way to reinitialize a slice i.e. completely empty it but keep it

You can reinitialize a slice by re-slicing all its items out

// Keep everything from [0 .. 0), which means keep nothing
arr = arr[:0]

But there's a problem in doing this : as stated above, the backing array of the slice will still reference to the original items that were in the slice. What you should do instead is create a new slice and let this one be garbage collected.

答案2

得分: 1

答案有多个方面:

  1. 你必须意识到,没有没有支持数组的切片,如果你谈论一个切片,你总是要考虑到支持数组。对此进行一些思考会得出以下结论...
  2. 问题的第二部分“重新初始化一个切片,即完全清空它但仍保留它”非常不清楚。不要以这种方式思考切片。a = a[:0]将切片a重新切片为零长度,同时保留支持数组。
  3. 对于其他所有情况:请参考“官方”的切片技巧https://code.google.com/p/go-wiki/wiki/SliceTricks
英文:

The answer is manyfold:

  1. You must realize that there is no slice without backing array and if you talk about a slice you always have to think about the backing array too. Musing about this a bit leads to...
  2. The second part of the question "reinitialize a slice i.e. completely empty it but still keep it" is very unclear. Do not think about slices in this way. a = a[:0] reslices a to zero length while keeping the backing array.
  3. For everything else: Have a look at the "official" Slice Tricks https://code.google.com/p/go-wiki/wiki/SliceTricks

huangapple
  • 本文由 发表于 2013年8月5日 07:02:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/18048473.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定