在遍历slice的时候删除其元素导致的panic “slice bounds out of range”

//删除slice中的3,6,9
func test1() {
    slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    for i, n := range slice {
        if n%3 == 0 {
            slice = append(slice[:i], slice[i+1:]...)
        }
    }
    fmt.Printf("%v\n", slice)
}

运行后报了一个panic出来:

panic: runtime error: slice bounds out of range

goroutine 1 [running]:
panic(0x5189c0, 0xc082002040)
        D:/Go/src/runtime/panic.go:464 +0x3f4
main.test4()
        E:/Code/go/hello.go:79 +0x395
main.main()
        E:/Code/go/hello.go:16 +0x20
exit status 2

是不是这种情况不应该使用这种方式来删除元素?
slice = append(slice[:i], slice[i+1:]...)

如果不是应该用什么方式来删除呢?

共 7 个回复


heimeil

range里面就不应该再修改原始slice了,可以另外新建一个slice

# 0

luxor

s0 := []int{1,2,3}

for range等价于
s1 := s0
for i:=0;i<s1.len;i++{}

for 循环中s0删除后,s0.len也变小了,但s1.len还是原来的值,自然就越界了

# 1

mnhkahn

很有意思,map就支持在遍历的时候删除

# 2

ayanmw


func test1() {
    slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
restart:
    for i, n := range slice {
        if n%3 == 0 {
            slice = append(slice[:i], slice[i+1:]...)
            goto restart
        }
    }
    fmt.Printf("%v\n", slice)
}

加一个goto吧。这是我想到的最容易的办法了。
1.C++ STL的map支持修改迭代器,但是golang的range 不算迭代器,怎么修改呢?

  1. golang 真变态,如果slice是偶数就不报错,如果是奇数个就报错。(上面slice 如果有偶数个就不报错)
# 4

ayanmw

我有想到了一种方案,就是 将需要删除的元素放到最后,然后最后一次性删除,减少遍历次数;
代码如下:

func test1() {
    slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Printf("%v\n", slice)
    deleteNum:=0
    var j int=len(slice)-1
    for i:=0;i<len(slice) && i<j;i++{
        if slice[i]%3 == 0 {
            fmt.Println("index ",i," deleteNum++ slice[i]=",slice[i])
            deleteNum++
            for j>i{
                if slice[j]%3!=0{
                    //交换 把要删除的移动到最后
                    slice[i],slice[j]=slice[j],slice[i]
                    fmt.Println("index i=",i,"j=",j," 进行swap ",slice[j],slice[i])
                    fmt.Println("test and break",deleteNum)
                    j--
                    break
                }else{
                    //后面的也是要删除的,删除计数+1
                    deleteNum++
                    fmt.Println("index ",i,j," deleteNum++ =",deleteNum," slice[j]=",slice[j])
                }
                j--
            }
        }
    }
    if(deleteNum>0){
        slice=slice[:len(slice)-deleteNum]
    }
    fmt.Printf("%v\n", slice)
}

缺点就是顺序变了,不过这个应该不是什么大问题的吧。

# 5

xixi~

#4 4楼牛牛的

# 6