删除切片中的元素

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

}

该程序输出:


[5 7] <- as expected


[5 7 7] <- why not [5 6 7] or [5 7] ?


一只名叫tom的猫
浏览 105回答 1
1回答

湖上湖

切片值只是标头,指向后备数组。切片头只包含指针。所以当你复制一个切片值时,副本也会指向同一个后备数组。因此,如果您通过原始切片标头更改支持数组,则副本也会观察到更改。这就是你的情况。你分配area1给area2. 单元格是一个切片数组。因此将复制数组,其中包含切片标头,因此将复制切片标头。切片头包含指向支持数组的指针,支持数组不会被复制。所以只有一个支持数组来保存[5, 6, 7]元素。然后调用removeValueFromCell(),它会修改这个后备数组:Before: [5, 6, 7] After: [5, 7, 7]因为该元素6已被删除,所以切片的其余部分(元素[7])被复制以代替被删除的元素。然后您将这个新的切片标头(正确地只包含 2 个元素)分配给area1.Cells[1].但是 slice 值area2.Cells[1]指向同一个后备数组,并且由于您没有触及这个 slice 值,它的长度仍然是3,所以它会看到所有后备数组都更改了元素:[5, 7, 7]。另请注意,您的 实现removeValueFromCell()是错误的,因为如果可移动元素在切片中多次列出,它的行为将不正确。这样做的原因是因为当您删除一个元素时,后续元素的索引会移动(减少 1),但您的循环变量不会考虑到这一点。最容易处理的是使用向下循环。
打开App,查看更多内容
随时随地看视频慕课网APP