删除切片中的元素

sax*_*sax 5 arrays go slice

Go不提供任何高级函数来从切片中删除元素.我编写了一个函数,它以一种通常在这里建议的方式从切片中删除给定值,但它产生了非常意外的结果.

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
}
Run Code Online (Sandbox Code Playgroud)

该计划输出:

[5 7] <- as expected

[5 7 7] <- why not [5 6 7] or [5 7] ?
Run Code Online (Sandbox Code Playgroud)

icz*_*cza 6

切片值只是标题,指向后备数组.切片标头仅包含指针.因此,复制切片值时,副本也将指向相同的后备阵列.因此,如果您通过原始切片标头更改后备阵列,则副本也会观察更改.

这就是你的情况.你分配area1area2.Cells是一个切片数组.因此将复制数组,其中包含切片标头,因此将复制切片标头.Sice头包含指向后备阵列的指针,后备阵列不会重复.

因此只有一个支持[5, 6, 7]元素的支持数组.然后调用removeValueFromCell(),它将修改这个后备数组:

Before:
[5, 6, 7]
After:
[5, 7, 7]
Run Code Online (Sandbox Code Playgroud)

因为元素6已被删除,所以切片的其余部分(元素[7])被复制以代替被移除的元素.

并且您将此新切片标头(正确地仅包含2个元素)分配给area1.Cells[1].

但切片值area2.Cells[1]指向同一个支持数组,并且由于您没有触及此切片值,因此它的长度为3,因此它将看到所有支持数组更改的元素:[5, 7, 7].

另请注意,您的实现removeValueFromCell()是错误的,因为如果可移动元素在切片中多次列出,则表现不正确.这样做的原因是当你删除一个元素时,后续元素的索引会被移位(变得小于1),但是你的循环变量并没有考虑到这一点.最容易处理的是使用向下循环.有关详细信息,请参阅如何在golang中删除循环中struct数组的元素.