Delete data from ArrayList with a For-loop

I got a weird problem. I thought this would cost me few minutes, but I am struggling for few hours now... Here is what I got:

for (int i = 0; i < size; i++){
    if (data.get(i).getCaption().contains("_Hardi")){
        data.remove(i);
    }
}

The data is the ArrayList. In the ArrayList I got some strings (total 14 or so), and 9 of them, got the name _Hardi in it.

And with the code above I want to remove them. If I replace data.remove(i); with a System.out.println then it prints out something 9 times, what is good, because _Hardi is in the ArrayList 9 times.

But when I use data.remove(i); then it doesn't remove all 9, but only a few. I did some tests and I also saw this:

When I rename the Strings to: Hardi1 Hardi2 Hardi3 Hardi4 Hardi5 Hardi6

Then it removes only the on-even numbers (1, 3, 5 and so on). He is skipping 1 all the time, but can't figure out why.

How to fix this? Or maybe another way to remove them?


The Problem here is you are iterating from 0 to size and inside the loop you are deleting items. Deleting the items will reduce the size of the list which will fail when you try to access the indexes which are greater than the effective size(the size after the deleted items).

There are two approaches to do this.

Delete using iterator if you do not want to deal with index.

for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if (it.next().getCaption().contains("_Hardi")) {
    it.remove();
}
}

Else, delete from the end.

for (int i = size-1; i >= 0; i--){
    if (data.get(i).getCaption().contains("_Hardi")){
            data.remove(i);
    }
 }

You shouldn't remove items from a List while you iterate over it. Instead, use Iterator.remove() like:

for (Iterator<Object> it = list.iterator(); it.hasNext();) {
    if ( condition is true ) {
        it.remove();
    }
}

Every time you remove an item, you are changing the index of the one in front of it (so when you delete list[1], list[2] becomes list[1], hence the skip.

Here's a really easy way around it: (count down instead of up)


for(int i = list.size() - 1; i>=0; i--)
{
  if(condition...)
   list.remove(i);
}


Its because when you remove an element from a list, the list's elements move up. So if you remove first element ie at index 0 the element at index 1 will be shifted to index 0 but your loop counter will keep increasing in every iteration. so instead you of getting the updated 0th index element you get 1st index element. So just decrease the counter by one everytime you remove an element from your list.

You can use the below code to make it work fine :

for (int i = 0; i < data.size(); i++){
    if (data.get(i).getCaption().contains("_Hardi")){
        data.remove(i);
        i--;
    }
}

It makes perfect sense if you think it through. Say you have a list [A, B, C]. The first pass through the loop, i == 0. You see element A and then remove it, so the list is now [B, C], with element 0 being B. Now you increment i at the end of the loop, so you're looking at list[1] which is C.

One solution is to decrement i whenever you remove an item, so that it "canceles out" the subsequent increment. A better solution, as matt b points out above, is to use an Iterator<T> which has a built-in remove() function.

Speaking generally, it's a good idea, when facing a problem like this, to bring out a piece of paper and pretend you're the computer -- go through each step of the loop, writing down all of the variables as you go. That would have made the "skipping" clear.