当需要对一个集合遍历删除元素的时候,都应该倒着删


这个也是我在实际写东西的时候,发现的一个比较有意义的经验。我对比了jsvascript和C#,发现“当需要对一个集合遍历删除元素的时候,都应该倒着删”这句话实在是一般无二。

                        int nowRemoveIndex = combination.Count() - 1;
                        while (true)
                        {
                            if (combination.Count() > lastCombitionCount)
                                combination.RemoveAt(nowRemoveIndex);
                            else
                                break;
                            nowRemoveIndex--;
                        }                    

上面这小段代码,是C#的,其中combination表示的是一个List集合。

这段代码的场景指的是,我目前有一个好几层的递归,每一层都会往这个集合插入数据,但是当递归在回归的时候,我发现这不是我要的数据,于是我就要把他从尾部添加上去的数据删掉。

这个很轻易的就可以想到,这个肯定就是从后往前遍历,然后逐个删除。

接下来再看一段JavaScript代码:

    let machineId = app.globalData.machineId
    for (let i = cartCache.length - 1; i >= 0; i--) {
      if (cartCache[i].MachineID === machineId) {
        cartCache.splice(i, 1)
        console.log('已清理一条缓存:')
        console.log(cartCache[i])
      }
    }

这是小程序中的一段js代码。

当时的代码场景是,假设我目前有好几个购物车,machineId可以看成是分类id,一个分类对应一个购物车(业务场景是虚构的,请不要较真)。但是,我的购物车数据是全部丢到一个集合里面,即上面的cartCache。那现在我要清空其中一个购物车,这个时候就是必须得遍历cartCache,然后用machineId这个标识来找到我要清除的对象。

这里可以看到,我是从后往前遍历,然后才使用splice删除元素的。

现在来做个实验,正着删和倒着删的结果会不会不一样

    let testArray1 = [1,3,4,5,2,7]
    let testArray2 = [1,3,4,5,2,7]
    for (let i = 0; i < testArray1.length; i++) {
      if(testArray1[i] >= 4)
      testArray1.splice(i,1)      
    }
    for (let i = testArray2.length - 1; i >= 0; i--) {
      if(testArray2[i] >= 4)
      testArray2.splice(i,1)   
    }
    console.log('testArray1的结果为:')
    console.log(testArray1)
    console.log('testArray2的结果为:')
    console.log(testArray2)

上面还是两个普通的js for循环遍历数组然后删除元素,不同的是,一个是正着删,还一个则是倒着删,其结果如下:

 现在思考的时候来了,为什么正着删会多出一个5没删掉呢?

这个是因为,当循环遍历到4的时候,此时 i 的值等于2,在执行完splice的之后,4这个元素被删除了,而 i 顺利成章的 ++,然后变成了 3,而此时索引 3 对应的元素是 2 ,因为4被删除了,所以5变成了刚刚4的位置,正好就这么把5给漏了。

以上是用JavaScript做的演示,关于其他语言,可以自己多尝试。对于C#,如果正着删的话,代码运行的时候是会直接报错。