在GO lang中删除和添加元素到数组

hgqdbh6s  于 2023-09-28  发布在  Go
关注(0)|答案(3)|浏览(131)

我有两个数组声明为:var input []stringvar output []string
输入数组最初用一些ID填充。输出数组为NULL。
每次迭代后,我想从输入数组中删除一个随机元素,并将其添加到输出数组中。
最后,输出数组中的所有元素将与输入数组相同(但具有不同的顺序(索引))。

for index := 0; index < len(input); index++ {
    if !visited[index] {
        //do something
    }
}
output[#iteration index] = input[current index]

当我尝试这样做时,我得到array out of bounds error

aiqt4smr

aiqt4smr1#

对于output阵列,您需要使用append或为其分配初始容量以匹配input的大小。

// before the loop
output := make([]string, len(input))

这将是我的建议,因为append会导致一堆不必要的重新分配,并且您已经知道您需要什么容量,因为它基于input
另一件事是:

output = append(output, input[index])

但就像我说的,从我观察到的情况来看,append使初始容量呈指数级增长。如果你没有指定任何东西,这将是基数2,这意味着你将在达到所需的容量之前进行几次不必要的重新分配。

vxqlmq5t

vxqlmq5t2#

你可以在golang/SliceTricks找到一些有用的技巧。
自从引入了append内置,在Go 1中删除的container/vector包的大部分功能可以使用appendcopy复制。
以下是向量方法及其切片操作类似物:

a = append(a, b...)

复制

b = make([]T, len(a))
copy(b, a)
// or
b = append([]T(nil), a...)

剪切

a = append(a[:i], a[j:]...)

删除

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

删除不保留顺序

a[i] = a[len(a)-1] 
a = a[:len(a)-1]

注意如果元素的类型是 pointer 或带指针字段的struct,需要进行垃圾收集,则上述CutDelete的实现存在潜在的 * 内存泄漏 * 问题:一些具有值的元素仍然被切片a引用,因此不能被收集。下面的代码可以解决这个问题:

copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
    a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]

删除

copy(a[i:], a[i+1:])
a[len(a)-1] = nil // or the zero value of T
a = a[:len(a)-1]

删除不保留顺序

a[i] = a[len(a)-1]
a[len(a)-1] = nil
a = a[:len(a)-1]

展开

a = append(a[:i], append(make([]T, j), a[i:]...)...)

扩展

a = append(a, make([]T, j)...)

插入

a = append(a[:i], append([]T{x}, a[i:]...)...)

注意第二个append使用自己的底层存储创建一个新切片,并将a[i:]中的元素复制到该切片,然后这些元素被复制回切片a(由第一个append)。可以通过使用替代方式来避免创建新切片(以及因此的内存垃圾)和第二副本:
插入

s = append(s, 0)
copy(s[i+1:], s[i:])
s[i] = x

插入向量

a = append(a[:i], append(b, a[i:]...)...)

弹出

x, a = a[0], a[1:]

弹出后

x, a = a[len(a)-1], a[:len(a)-1]

a = append(a, x)

推前

a = append([]T{ x }, a...)

移位

x, a := a[0], a[1:]

取消移位

a = append([]T{x}, a...)

附加技巧

过滤不分配

这个技巧利用了这样一个事实,即一个切片与原始切片共享相同的备份数组和容量,因此存储空间被过滤后的切片重用。当然,原始内容被修改了。

b := a[:0]
for _, x := range a {
    if f(x) {
        b = append(b, x)
    }
}

反转

要用相同的元素但以相反的顺序替换切片的内容,请执行以下操作:

for i := len(a)/2-1; i >= 0; i-- {
    opp := len(a)-1-i
    a[i], a[opp] = a[opp], a[i]
}

同样的事情,除了两个索引:

for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
    a[left], a[right] = a[right], a[left]
}

Shuffle

Fisher-Yates算法:

for i := len(a) - 1; i > 0; i-- {
    j := rand.Intn(i + 1)
    a[i], a[j] = a[j], a[i]
}
o3imoua4

o3imoua43#

在Go 1.21中,插入,删除(以及更多!)功能可用于切片

示例

插入

package main

import (
    "fmt"
    "slices"
)

func main() {
    names := []string{"Alice", "Bob", "Vera"}
    names = slices.Insert(names, 1, "Bill", "Billie")
    names = slices.Insert(names, len(names), "Zac")
    fmt.Println(names)
}

删除

package main

import (
    "fmt"
    "slices"
)

func main() {
    letters := []string{"a", "b", "c", "d", "e"}
    letters = slices.Delete(letters, 1, 4)
    fmt.Println(letters)
}

相关问题