views:

454

answers:

6

The following code is just to produce an example of the problem:

 public static void main(String[] args) {
  Collection<Integer> src = new ArrayList<Integer>();
  Collection<Integer> dest = new ArrayList<Integer>();

  src.add(2);
  src.add(7);
  src.add(3);
  src.add(2201);
  src.add(-21);

  dest.add(10);

  while (src.size() != 0) {
   for (int i : dest) {
    int min = Collections.min(src);
    dest.add(min);
    src.remove(min);
   }
  }

 }

What I want to do is move everything from src to dest in a specific order. (Here, it's what is the minimum value, but that's just a simplification from my real problem.) However, I am modifying dest while iterating over it, and get the following error:

Exception in thread "main" java.util.ConcurrentModificationException
 at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
 at java.util.AbstractList$Itr.next(Unknown Source)
 at nth23.experimental.MoveBetweenSets.main(MoveBetweenSets.java:25)

How can I get around this?

+1  A: 

You can remove from a collection (well, some collections) while iterating over it using iterator.remove() - but you can't usually add to it.

However, as newacct points out in comments, the ListIterator interface does include an add method, so you should be able to change your code like this:

public static void main(String[] args) {
  Collection<Integer> src = new ArrayList<Integer>();
  List<Integer> dest = new ArrayList<Integer>();

  src.add(2);
  src.add(7);
  src.add(3);
  src.add(2201);
  src.add(-21);

  dest.add(10);

  while (src.size() != 0) {
   for (ListIterator<Integer> li = dest.listIterator(); li.hasNext() ;) {
    int min = Collections.min(src);
    li.add(min);
    src.remove(min);
   }
  } 
 }

Note that now dest has to be declared as List rather than Collection, and you need to expand the for loop explicitly. However, I'm still not sure why you're iterating over dest in the first place. You're adding an element on every iteration, so you'll never reach the end.

What's wrong with this?

  while (src.size() != 0) {
    int min = Collections.min(src);
    dest.add(min);
    src.remove(min);
  }

Or, as others have said, just call sort() - passing in a custom Comparator if you need to.

Jon Skeet
That's not true. You can add to Lists and change elements if you are iterating over them with a ListIterator.
newacct
Ooh, you're absolute right. Had forgotten about that bit of ListIterator. Will edit.
Jon Skeet
A: 

As you've seen, you cannot change a collection while you iterate over it. (To be more precise, you can change it, but you can't continue iterating)

You can either iterate over a copy of the list or use a traditional for loop.

Either way, make sure that you understand exactly what happens to the indices as you modify the collection; otherwise, your code won't work correctly.

For more specific advice, tell us what you're actually doing.

SLaks
A: 

You could create temporary lists where you keep track of what should be added and removed without actually making changes to dest and src. Then, outside of the loop use the temporary lists to add and remove necessary items. But like Jon Skeet said, more specific requirements would help. I assume there are some limitations.

Carlos
+2  A: 

Is there a reason why you can't just copy the source list to the destination list and then sort it?

Collection<Integer> dest = new ArrayList<Integer>(src);
Collections.sort(dest);
uckelman
+2  A: 

This is a workaround:

while (!src.isEmpty()) {
    int min = Collections.min(src);
    dest.add(min);
    src.remove(min);
}

But this might be even make thing worse. Be a bit more specific (as Jon mentioned).

dz
+1  A: 

To be honest, I don't get the for (int i : dest) part. And if you remove it, there is actually no problem and this answers the question :)

Pascal Thivent