在for循环中更改列表

时间:2016-02-01 12:17:11

标签: c# list for-loop

我正在尝试修改for value中的列表

for (int i = 0; i < theList.Count; i++) {
    if(someCircunstances)
        theList.remove(component);
    else
        theList.add(component);
}

我使用此方法获得ArgumentOutOfRangeException。 有没有办法来实现这个目标?

7 个答案:

答案 0 :(得分:2)

在迭代列表时改变列表也是一种不好的做法。

这是另一种选择:

theList.RemoveAll(someCircunstances);

答案 1 :(得分:2)

可以通过向后迭代并使用索引而不是项来解决:

for (int i = list.Count - 1; i > 0; i--)
{
    if(condition)
        list.RemoveAt(i);
    else
        list.Add(component);
}

一些解释:当您对集合进行迭代时,不应更改范围中的项目。迭代器将检测到并抛出(如果是foreach,则必须使用列表副本)。但是在使用索引(RemoveAt()方法)和向后迭代的情况下,您可以安全地进行下一次迭代,但范围不包括已删除的项目。 Add()正在添加到最后,因此新项目永远不会在范围内。

我将添加更多解决方案,其中一个更好地决定自己:

经典 foreach,先复制:

foreach(var item in list.ToArray()) // imho `ToArray` is better than `ToList`
    if(condition)
        list.Remove(item);
    else
        list.Add(component);

结果新列表:

var result = new List<...>();
foreach(var item in list)
    result.Add(condition ? component : item); // not sure here, but should give you idea
list = result;

答案 2 :(得分:0)

您将超出范围异常,因为索引从0开始。

如上所述,一种解决方案是从List.count中删除1,另一种解决方案是在1而不是0时启动i。

想一想:如果你的列表中有1个元素,那个元素的索引是0,如果你有100个元素,你的hundreth元素的索引是99。

你正在考虑如下列表:[1] [2] [3],而它实际上是[0] [1] [2]

答案 3 :(得分:0)

这里的问题是你要删除列表中的值,然后使用已经删除的索引再次迭代它 - &gt; ArgumentOutOfRangeException

所以要解决这个问题,我建议你把它分成两个for循环:

for (int i = theList.Count - 1; i >= 0; i--) {
    if(someCircunstances)
        theList.remove(component);
}

for (int i = 0; i < theList.Count; i++) {
    if(someCircunstances)
        theList.add(component);
}

答案 4 :(得分:0)

我同意Tamas,在迭代时不要改变列表,还有另一种方法来实现你的观点

 List<someType> ToRemove=new List<someType>() ; //some type is same type as theList is  
 List<someType> ToAdd=new List<someType>();

for (int i = 0; i < theList.Count; i++) {
 if(someCircunstances)
     ToRemove.add(component);
 else
     ToAdd.add(component);
}

 theList=((theList.Except(ToRemove)).Concat(ToAdd)).ToList();

答案 5 :(得分:0)

根据评论,您需要能够为新创建的项目应用相同的逻辑。

你需要做这样的事情:

public void DoIt(List<MyObject> theList)
{
    List<MyObject> items_to_remove = new List<MyObject>();

    List<MyObject> items_to_add = new List<MyObject>();

    for (int i = 0; i < theList.Count; i++)
    {
        if (someCircunstances)
            items_to_remove.Add(....); //Remove some existing item
        else
            items_to_add.Add(....); //Add a new item
    }

    if(items_to_remove.Count > 0)
        items_to_remove.ForEach(x => theList.Remove(x));

    if (items_to_add.Count > 0)
    {
        DoIt(items_to_add); //Recursively process new objects 

        theList.AddRange(items_to_add);
    }
}

您的想法是在自己的列表中插入要添加的项目和要删除的项目。

然后在迭代后,删除需要删除的项目。

之后,您需要添加要添加的项目。但是,在这之前你需要对它们运行相同的逻辑,这就是递归调用的解释。

请注意我使用的是MyObject,因为我不知道您的列表类型。使用您正在使用的任何类型。

答案 6 :(得分:0)

如果你可以使用循环的当前索引从lst中删除项目,你可以这样轻松地执行此操作:

using System;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            var numbers = Enumerable.Range(1, 20).ToList();
            var rng = new Random();

            for (int i = 0; i < numbers.Count; ++i)
            {
                if (rng.NextDouble() >= 0.5) // If "someCircumstances"
                {
                    numbers.Add(numbers[i]*2);
                }
                else
                {
                    // Assume here you have some way to determine the 
                    // index of the item to remove. 
                    // For demo purposes, I'll just calculate a random index.

                    int index = rng.Next(0, numbers.Count);

                    if (index >= i)
                        --i;

                    numbers.RemoveAt(index);
                }
            }

            Console.WriteLine(string.Join("\n", numbers));
        }
    }
}

这也会循环添加到列表末尾的所有数字。 numbers.Count的值在每次迭代时重新计算,因此当它更改时,循环将适当地扩展。

(Offtopic)奖金问题:在上面的代码中,当循环退出时,列表的平均大小是多少?什么是最大尺寸?