81

I'm trying to remove an item from an ArrayList and I get this Exception:
Collection was modified; enumeration operation may not execute.

Any ideas?

Kay Lee
  • 922
  • 1
  • 12
  • 40
Ricardo
  • 11,609
  • 8
  • 27
  • 39
  • 11
    For the record, unless you are using .NET 1.0 or 1.1, you should probably be using `List` instead of `ArrayList`. – Marc Gravell Jan 07 '10 at 22:37
  • 2
    Why is this duplicate I get this error **sometimes** on `Application.Exit();` and I don't manipulate any collections nor Application. – Bitterblue Nov 19 '13 at 06:28
  • foreach(ItemCollection item in ItemCollection.Values) To foreach(ItemCollection s in ItemCollection.Values.ToList()) The issue is that ItemCollection.Values is being modified inside the foreach loop. Calling ItemCollection.Values.ToList() copies the values of subscribers.Values to a separate list at the start of the foreach – Niraj Trivedi Aug 01 '17 at 04:22

9 Answers9

197

You are removing the item during a foreach, yes? Simply, you can't. There are a few common options here:

  • use List<T> and RemoveAll with a predicate
  • iterate backwards by index, removing matching items

    for(int i = list.Count - 1; i >= 0; i--) {
        if({some test}) list.RemoveAt(i);
    }
    
  • use foreach, and put matching items into a second list; now enumerate the second list and remove those items from the first (if you see what I mean)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 4
    if the collection isn't that big, a simple .ToArray() and enumerating that instead might be good enough as well. – Lasse V. Karlsen Jan 07 '10 at 22:36
  • 2
    That's pretty neat, ive always been iterating forwards and conditionally decrementing `i` so that next time it increments it will point to the item after the deleted one. This avoids the extra step. – Igor Zevaka Jan 07 '10 at 22:57
  • 1
    Backwards iteration works fine. I'd avoid using .ToArray() as it adds an unnecessary loop to your algorithm (one to copy the list to an array, one to remove the items you're interested in). – 3Dave Jan 08 '10 at 17:50
  • add .ToList(); to the array in the foreach then you can do as you want – conners Nov 22 '12 at 21:10
  • Hi Marc, please advice why I still get this error even if I got my new list from first one, and enumerating new one I delete items from old one, but error still occurs. Only if I apply `ToList` to new list error disasppears. But don't enumerating new list already makes what `ToList` calling made? – Johnny_D Apr 30 '14 at 09:41
  • @Johnny_D would need to see example – Marc Gravell Apr 30 '14 at 11:15
  • Nice one, love the iterate backwards trick. – Aaron Jan 02 '16 at 23:44
  • @conners can you give an example? – Demodave Jun 12 '17 at 21:25
  • @MarcGravell Why can you not user remove in a foreach? – Demodave Jun 12 '17 at 21:28
  • @Demodave because that isn't a supported thing that you're allowed to do; iterating a collection that changes while you're looking at it gets very very messy, so most collections just say "nope, if you do that : I give up and throw" – Marc Gravell Jun 12 '17 at 21:36
  • Thanks, this worked for me. Though, I had to initialize the counter variable (i) at one lesser than the total list count to avoid runtime exceptions. – Vishal May 16 '18 at 19:54
24

Here's an example (sorry for any typos)

var itemsToRemove = new ArrayList();  // should use generic List if you can

foreach (var item in originalArrayList) {
  if (...) {
    itemsToRemove.Add(item);
  }
}

foreach (var item in itemsToRemove) {
  originalArrayList.Remove(item);
}

OR if you're using 3.5, Linq makes the first bit easier:

itemsToRemove = originalArrayList
  .Where(item => ...)
  .ToArray();

foreach (var item in itemsToRemove) {
  originalArrayList.Remove(item);
}

Replace "..." with your condition that determines if item should be removed.

Will
  • 1,561
  • 9
  • 6
  • 2
    If you're using "generic List" (which I interpret as `List`) then just use `list.RemoveAll(item => ...)`. – Marc Gravell Jan 07 '10 at 23:26
  • I'm using Blazor, and I was doing foreach then remove item, but was getting a funny error. Doing it this way resolved my funny error! + 1 – James Heffer Apr 30 '19 at 10:09
  • My experience was with a Dictionary. I grabbed the keys and iterated over those, but it still gave me the error when I tried to remove an element, even tough I wasn't iterating over the collection itself. I guess the .Keys property is a pointer to the actual dictionary, so it causes the same error. Making a second list of keys to remove is the answer. – Mageician Oct 17 '19 at 17:35
8

One way is to add the item(s) to be deleted to a new list. Then go through and delete those items.

ozczecho
  • 8,649
  • 8
  • 36
  • 42
8

I like to iterate backward using a for loop, but this can get tedious compared to foreach. One solution I like is to create an enumerator that traverses the list backward. You can implement this as an extension method on ArrayList or List<T>. The implementation for ArrayList is below.

    public static IEnumerable GetRemoveSafeEnumerator(this ArrayList list)
    {
        for (int i = list.Count - 1; i >= 0; i--)
        {
            // Reset the value of i if it is invalid.
            // This occurs when more than one item
            // is removed from the list during the enumeration.
            if (i >= list.Count)
            {
                if (list.Count == 0)
                    yield break;

                i = list.Count - 1;
            }

            yield return list[i];
        }
    }

The implementation for List<T> is similar.

    public static IEnumerable<T> GetRemoveSafeEnumerator<T>(this List<T> list)
    {
        for (int i = list.Count - 1; i >= 0; i--)
        {
            // Reset the value of i if it is invalid.
            // This occurs when more than one item
            // is removed from the list during the enumeration.
            if (i >= list.Count)
            {
                if (list.Count == 0)
                    yield break;

                i = list.Count - 1;
            }

            yield return list[i];
        }
    }

The example below uses the enumerator to remove all even integers from an ArrayList.

    ArrayList list = new ArrayList() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    foreach (int item in list.GetRemoveSafeEnumerator())
    {
        if (item % 2 == 0)
            list.Remove(item);
    }
Joe B
  • 873
  • 1
  • 9
  • 17
7

Don't modify the list inside of a loop which iterates through the list.

Instead, use a for() or while() with an index, going backwards through the list. (This will let you delete things without getting an invalid index.)

var foo = new List<Bar>();

for(int i = foo.Count-1; i >= 0; --i)
{
  var item = foo[i];
  // do something with item
}
3Dave
  • 28,657
  • 18
  • 88
  • 151
  • 1
    Snap! So, how should i be doing it? – Ricardo Jan 07 '10 at 22:33
  • 1
    Use a regular loop instead (for/while). The problem is using a 'foreach' statement (i.e. an enumerator). – Noon Silk Jan 07 '10 at 22:34
  • @silky f you're using a for() or while() to iterate through the collection, be sure to do it from the back/top - starting at collection.count-1 and going down to 0. Otherwise you'll hit the same problem. – 3Dave Jan 23 '10 at 07:35
  • 1
    A code sample showing what to do is a good idea when trying to answer a question. – theJerm Nov 30 '13 at 23:27
  • @theJerm and copying and pasting the same comment on every answer is a bad idea. – 3Dave Dec 01 '13 at 23:22
5

Am I missing something? Somebody correct me if I'm wrong.

list.RemoveAll(s => s.Name == "Fred");
KevinDeus
  • 11,988
  • 20
  • 65
  • 97
2

using ArrayList also you can try like this

ArrayList arraylist = ... // myobject data list

ArrayList temp = (ArrayList)arraylist.Clone();

foreach (var item in temp)
{
      if (...)
         arraylist.Remove(item);
}
Nikson Kanti Paul
  • 3,394
  • 1
  • 35
  • 51
2

Instead of foreach(), use a for() loop with a numeric index.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
2

I agree with several of the points I've read in this post and I've incorporated them into my solution to solve the exact same issue as the original posting.

That said, the comments I appreciated are:

  • "unless you are using .NET 1.0 or 1.1, use List<T> instead of ArrayList. "

  • "Also, add the item(s) to be deleted to a new list. Then go through and delete those items." .. in my case I just created a new List and the populated it with the valid data values.

e.g.

private List<string> managedLocationIDList = new List<string>();
string managedLocationIDs = ";1321;1235;;" // user input, should be semicolon seperated list of values

managedLocationIDList.AddRange(managedLocationIDs.Split(new char[] { ';' }));
List<string> checkLocationIDs = new List<string>();

// Remove any duplicate ID's and cleanup the string holding the list if ID's
Functions helper = new Functions();
checkLocationIDs = helper.ParseList(managedLocationIDList);

...
public List<string> ParseList(List<string> checkList)
{
    List<string> verifiedList = new List<string>();

    foreach (string listItem in checkList)
    if (!verifiedList.Contains(listItem.Trim()) && listItem != string.Empty)
        verifiedList.Add(listItem.Trim());

    verifiedList.Sort();
    return verifiedList;
}        
Brian Wells
  • 1,572
  • 1
  • 14
  • 12