Nested iterating through list followed by an event

2019-05-06 13:21发布

问题:

I'm trying to iterate throuh a list while already looping through it (nested loops). Consider the code below:

ArrayList<Integer> list = new ArrayList<Integer>(); // add some values to it

for(int i : list) { // ConcurrentModificationException

   Iterator iterator = list.iterator();

   while(iterator.hasNext()) {

      int n = iterator.next();

      if(n % i == 0) {
         iterator.remove();
      }

   }

}

The example above results in a ConcurrentModificationException. The condition to remove an element is, of course, just an example.

I'm sure I'm just missing something; but how should I construct a loop that achieves the same thing in Java without throwing an exception?

回答1:

Obviously modifying list when you iterate over it causing the execption. You can use another list to maintain the list of elements to be removed and remove them at the end.

ArrayList<Integer> list = new ArrayList<Integer>(); // add some values to it
ArrayList<Integer> del = new ArrayList<Integer>(); // Elements to be deleted

for(int i : list) { // ConcurrentModificationException
   Iterator iterator = list.iterator();
   while(iterator.hasNext()) {    
      int n = iterator.next();
      if(n % i == 0) {
          del.add(n);      
      }
   }
}

list.removeALL(del);


回答2:

Make the outer iteration iterate over a copy of the list.

for (int i : new ArrayList<>(list)) {

  Iterator<Integer> iterator = list.iterator();

  while (iterator.hasNext()) {

    int n = iterator.next();

    if (n % i == 0) {
      iterator.remove();
    }

  }

}


回答3:

You are getting ConcurrentModificationException because, while doing for loop you are trying to modify the list.

I am not sure whether following is elegant solution or not, but something like below may work:

       Iterator<Integer> iterator = list.iterator();
            int i=1;
            while (iterator.hasNext()) {

                int n = iterator.next();

                if (n % i == 0) {
                    iterator.remove();
                }
                i++;
            }


回答4:

You cannot remove an item from a list that is being iterated. One option is to add the items that you need to another list. Finally you have list with the items you need. Or you can iterate over the clone of your original list.



回答5:

i do some thing pretty similar to you. hav e alook at this code .

out:for(int h=0; h<tempAl.size(); h++) {
                        if(tempAl.get(0).split("\\s")[0].equals("OFF")){
                            tempAl.remove(0);
                            h=-1;
                            continue;
                        }
                        if(tempAl.get(h).split("\\s")[0].equals("ON")){
                            ONTime= tempAl.get(h);
               ///rest fof the code
    }

i think you could also change the index after removing element from the arraylist.



回答6:

I haven't tried, but either use:

List<Integer> list = new ArrayList<Integer>(); 
// add some values to it  
for(Iterator<Integer> iterator1 = list.iterator(); iterator1.hasNext();) { 
    int i = iterator1.next();
    for(Iterator<Integer> iterator2 = list.iterator(); iterator2.hasNext();){
        int n = iterator.next();        
        if(n % i == 0) {          
            iterator2.remove();       
        }     
    }  
} 

or if this still throws the ConcurrentModificationException (I'm not sure what happens if you use 2 iterators backed by the same list), then use:

List<Integer> list = new ArrayList<Integer>(); 
// add some values to it  
for(int i : new ArrayList(list)){ // copy list 
    ...
}


回答7:

foreach java syntax hides an iterator but as hiding it, it is not possible to call remove method on this one.

So I would do:

ArrayList<Integer> list = new ArrayList<Integer>(); // add some values to it

int count = 0;
for(Iterator<Integer> it = list.iterator();it.hasNext();count++){ //increments count++
   Integer currentInt = it.next();
   if(currentInt % count == 0){
     it.remove();
   }
}

You can see that the same feature is achieved without the need of a secondary iterator.

You can't iterate through the same list in the same time. To sum up, a modcount variable is used to detect unexpected change of itself everytime a list is parallely changed or iterated over. Leading thus to a ConcurrentModificationException. It appears very often with multithreaded environment and developers must be aware of it.

Furthermore, prefer use for loop instead while loop to iterate over a collection.

Why ?

Because with the while way, you let the iterator object still in scope after the loop whereas with for, it doesn't. A simple ackward call to it.next() would end up with a NoSuchElementException.

It is a best practice to keep ;)