Go 不提供任何高级函数来从 slice 中删除元素。我编写了一个函数,以此处通常建议的方式从 slice 中删除给定值,但它产生了意想不到的结果。
package main
import "fmt"
type Area struct {
Cells [2][]uint8
}
func main() {
var area1 Area
area1.Cells[1] = []uint8 {5, 6, 7}
area2 := area1
area1.Cells[1] = removeValueFromCell(area1.Cells[1], 6)
fmt.Println(area1.Cells[1])
fmt.Println(area2.Cells[1])
}
func removeValueFromCell(cell []uint8, value uint8) []uint8{
var res = cell
for i := 0; i < len(cell); i++ {
if cell[i] == value {
res = append(cell[:i], cell[i+1:]...)
}
}
return res
}
这个程序输出:
[5 7] <- as expected
[5 7 7] <- why not [5 6 7] or [5 7] ?
最佳答案
slice 值只是 header ,指向后备数组。 slice 头只包含指针。所以当你复制一个 slice 值时,副本也会指向同一个后备数组。因此,如果您通过原始 slice header 更改支持数组,副本也会观察到更改。
这就是您的情况。您将 area1
分配给 area2
。单元格是一个 slice 数组。因此将复制数组,其中包含 slice header ,因此将复制 slice header 。 slice 头包含指向支持数组的指针,支持数组不会被复制。
因此只有一个后备数组包含 [5, 6, 7]
元素。然后调用 removeValueFromCell()
,它会修改这个支持数组:
Before:
[5, 6, 7]
After:
[5, 7, 7]
因为元素 6
已被删除,并且 slice 的其余部分(元素 [7]
)被复制以代替删除的元素。
然后您将这个新的 slice header (正确地只包含 2 个元素)分配给 area1.Cells[1]
。
但是 slice 值 area2.Cells[1]
指向同一个后备数组,并且由于您没有触及这个 slice 值,它的长度仍然是 3
,因此它将看到所有后备数组已更改的元素:[5, 7, 7]
。
另请注意,您对 removeValueFromCell()
的实现是错误的,因为如果可移动元素在 slice 中多次列出,它的行为将不正确。这样做的原因是因为当您删除一个元素时,后续元素的索引会移动(减少 1),但您的循环变量不会考虑到这一点。最容易处理的是使用向下循环。有关详细信息,请参阅 How to remove element of struct array in loop in golang .
关于arrays - 删除 slice 中的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52094061/