-2

If have a workflow that removes elements of a List by a certain criteria. However certain items are skipped? Why is this happening?

List<Integer> listWithAge = new ArrayList<>();
int randomNumber = 100;
for (int i = 0; i < randomNumber; i++) {
    listWithAge.add(i);
}

// this is my loop    
for (int i = 0; i < listWithAge.size(); i++) {
    System.out.println(i);
    if ((listWithAge.get(i) % 3) == 2) listWithAge.remove(i);
}

Above code is my loop. I replaced my condition with something simpler. If I run this code my second loop only runs for 67 turns instead of 100.

Eva Lovia
  • 13
  • 2
  • What output are you seeing? What output are you expecting? – bsheps Mar 19 '21 at 01:55
  • In spite of the skipping of values, your resulting array would still be correct since the source values are from 0 to n. This means the only ones skipped would be those items divisible by 3. But they would not have been deleted anyway. So how did you discover that values were being skipped? – WJS Mar 19 '21 at 12:59

3 Answers3

0

It is problematic to iterate over a list and remove elements while iterating over it.

If you think about how the computer has to reconcile it, it makes sense...

Here's a thought experiment for you to go through.

If you have a list that is size 10 and you want to remove elements 1, 5, and 9 then you would think maybe the following would work:

List<String> listOfThings = ...some list with 10 things in it...;
list.remove(0);
list.remove(4);
list.remove(8);

However, after the first remove command, the list is only size 9.. Then after the second command, it's size has become 8. At this point, it hardly even makes sense to do list.remove(8) anymore because you're looking at an 8-element list and the largest index is 7.

You can also see now that the 2nd command didn't even remove the element now that you wanted.

If you want to keep this style of "remove as I go" syntax, the more appropriate way is to use Iterators. Here's an SO that talks about it and shows you the syntax you would need (see the question). It's easy to read up on elsewhere too.

How Iterator's remove method actually remove an object

Atmas
  • 2,389
  • 5
  • 13
0

Skipping a value would be the result of your list getting out of sync with your loop index because the list is reduced in size. This causes you to hop over some locations since the reduction in size affects future locations that have not been reached.

So the first thing you could do is simply correct the synchronization by decrementing i when you remove a value from the list. This will keep index at the same spot as the list shifts "left" caused by the removal.

for (int i = 0; i < listWithAge.size(); i++) {
    if ((listWithAge.get(i) % 3) == 2) listWithAge.remove(i--);
}

The other option is to loop thru the list backwards.

for (int i = listWithAge.size()-1; i >= 0; i--) {
        if ((listWithAge.get(i) % 3) == 2) {
              listWithAge.remove(i);
        }
}   

This way, no values should be skipped since the removing of the element does affect the loop index's future positions relative to the changing size of the list.

But the best way would be to use an iterator as has already been mentioned by Atmas

As a side note, I recommend you always use blocks {} even for single statements as I did above in the if block. It will save you some serious debugging time in the future when you decide you need to add additional statements and then wonder why things are no longer working.

And deleting like this from a list is very expensive, especially for large lists. I would suggest that if you don't have duplicate values, you use a Set. Otherwise, instead of deleting matching values, add the non-matching to a second list.

WJS
  • 36,363
  • 4
  • 24
  • 39
0
List<Integer> listWithAge = new ArrayList<>();

int randomNumber = 100;

 for (int i = 0; i < randomNumber; i++) {

  listWithAge.add(i);

 }

// this is my loop  

List<Integer> itemsToBeDeleted = new ArrayList<>();

for (int i = 0; i < listWithAge.size(); i++) {

 System.out.println(i);

 if ((listWithAge.get(i) % 3) == 2) {

  itemsToBeDeleted.add(i);

 }

//delete all outside the loop

//deleting inside the loop messes the indexing of the array

listWithAge.removeAll(itemsToBeDeleted);
Luke Matafi
  • 91
  • 1
  • 5