【问题标题】:Removing elements in a slice删除切片中的元素
【发布时间】:2019-02-05 05:21:59
【问题描述】:

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

【问题讨论】:

  • 当您找到匹配项时,您并没有跳出 @9​​87654324@ 循环,也没有更新 i 以说明数组末尾在删除,如果您有意不跳出循环以查找更多匹配项,则需要执行此操作。
  • 阿德里安谢谢。你是对的,我在编写其他更大的代码时遇到了这个问题,所以我尽可能地修剪和简化代码以复制问题

标签: arrays go slice


【解决方案1】:

切片值只是标题,指向一个后备数组。切片头只包含指针。因此,当您复制切片值时,副本也将指向同一个后备数组。因此,如果您通过原始切片头更改后备数组,副本也会观察到更改。

这就是您的情况。您将area1 分配给area2。 Cells 是一个切片数组。因此将复制包含切片标头的数组,因此将复制切片标头。切片头包含指向后备数组的指针,后备数组不会被复制。

所以只有一个支持数组保存[5, 6, 7] 元素。然后调用removeValueFromCell(),它会修改这个后备数组:

Before:
[5, 6, 7]
After:
[5, 7, 7]

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

然后您将这个新的切片标头(正确地只包含 2 个元素)分配给 area1.Cells[1]

但是切片值area2.Cells[1] 指向同一个后备数组,并且由于您没有触及这个切片值,它仍然具有3 的长度,因此它会看到所有后备数组更改了元素: [5, 7, 7].

还请注意,removeValueFromCell() 的实现是错误的,因为如果可移动元素将在切片中多次列出,它的行为将不正确。这样做的原因是当您删除一个元素时,后续元素的索引会移动(减少 1),但您的循环变量没有考虑到这一点。最容易处理的是使用向下循环。详情请见How to remove element of struct array in loop in golang

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-16
    相关资源
    最近更新 更多