/

Golang 切片中删除元素

错误复现

话不多说,先上代码:

package main

import "fmt"

func main() {
    a := []int{1, 2, 3, 4, 5, 5, 6, 5, 7, 8, 9, 10}
    fmt.Println("First Slice: ", a)

    for i, value := range a {
        if value == 5 {
            a = append(a[:i], a[i+1:]...)
            fmt.Println("Index of deleted element: ", i)
            fmt.Println("Slice after delete: ", a)
        }
    }

    fmt.Println("Final Slice: ", a)
}

结果如下:

First Slice:  [1 2 3 4 5 5 6 5 7 8 9 10]
Index of deleted element:  4
Slice after delete:  [1 2 3 4 5 6 5 7 8 9 10]
Index of deleted element:  6
Slice after delete:  [1 2 3 4 5 6 7 8 9 10]
Final Slice:  [1 2 3 4 5 6 7 8 9 10]

问题分析

看出问题了吗?让我们来理清一下思路。

首先,我们定义了一个数组切片,其中有若干个重复的 5 。代码的目的是找出数组中所有的 5 并且删除。那么代码的主要思路就是遍历整个数组,然后找出所有值为 5 的元素, 将其前后的所有元素作为新的切片重写原来的切片。

这时候问题出现了,当找到一个值为 5 的元素时,我们试图通过 append(a[:i], a[i+1:]...) 来删除这个元素。但是,由于切片操作 a[:i] 和 a[i+1:] 都是在原始的 a 切片上进行的。当删除一个元素后,索引 i 后面所有的元素都会向前移动一个位置。这意味着在下一次循环迭代时,索引 i 将指向下一个 5,但由于索引已经改变,原来的 i+1 处的元素现在变成了 i 处的元素,导致这个 5 被跳过不被删除。

也就是说,当在循环中删除元素时,切片的长度会改变,这会影响后续迭代的索引。因此,删除元素后,循环中的索引可能会跳过一些元素或重复处理某些元素。

解决方案

i 回退

理所应当的,最先想到的解决方法就是这个了。在执行完 append(a[:i], a[i+1:]...) 后,再执行一次 i-- 语句来调整索引,这样在删除元素后,下一次迭代会检查当前索引 i 后面的元素,而不是跳过它。

两个切片

可以新建一个切片,用于存储需要保留的所有元素,最后再给切片 a 赋值。

package main

import "fmt"

func main() {
    a := []int{1, 2, 3, 4, 5, 5, 6, 5, 7, 8, 9, 10}
    fmt.Println("First Slice: ", a)

    var result []int
    for _, value := range a {
        if value != 5 {
            result = append(result, value)
        }
    }

    a = result
    fmt.Println("Final Slice: ", a)
}

这种方法有几个优点:

  • 代码简洁,易于理解。
  • 不在循环中修改切片,从而避免了索引问题。

但是如果原切片很大,这种方法可能会占用更多内存,造成内存损耗。

直接赋值

请看代码:

package main

import "fmt"

func main() {
    a := []int{1, 2, 3, 4, 5, 5, 6, 5, 7, 8, 9, 10}

    i := 0
    for _, v := range a {
        if v != 5 {
            a[i] = v
            i++
        }
    }

    a = a[:i]
    fmt.Println("Final Slice: ", a)
}

这种方法直接在原切片上做修改,避免了前一种方法中,需要额外新建一个切片而带来可能的内存隐患

逆序遍历

这种方法比较抽象,从后面往前面遍历,不过正经人应该不会这样写吧🤣


package main

import "fmt"

func main() {
    a := []int{1, 2, 3, 4, 5, 5, 6, 5, 7, 8, 9, 10}

    for i := len(a) - 1; i >= 0; i-- {
        if a[i] == 5 {
            a = append(a[:i], a[i+1:]...)
        }
    }

    fmt.Println("Final Slice: ", a)
}

书影音

    • 读完:蛤蟆先生去看心理医生@罗伯特·戴博德|★★★★☆
  • 电影

    • 看完:谈判专家|★★★☆☆
  • 音乐

    • 在听:哭砂@陈粒&张杰|★★★★☆
    • 在听:时候@苏运莹|★★★★☆