Another kind of ConcurrentModificationException, how can I solve it?

B

Bill David

In my application, I will iterate a map and send out a message to
another class with the key of current object to which iterator is
pointing. And the operation is in the same thread. This class get the
notification will do more handle and then, it will try to remove this
object from the map with the key.
But when I try to go on to the next object in the map, I will get the
ConcurrentModificationException.

I know if I call Iterator.remove during iterating, no exception will
happen. But I can't do that, as I must ensure the other class has
completely handled the notification.

// OK, but not what I want
Map<Integer, String> sessions = plugin.getSessions();
Set<Map.Entry<Integer, String>> set = sessions.entrySet();
for (Iterator<Map.Entry<Integer, String>> it = set.iterator();
it.hasNext();) {
// send out notification...
it.remove();
}

// Failed
Map<Integer, String> sessions = plugin.getSessions();
Set<Map.Entry<Integer, String>> set = sessions.entrySet();
for (Iterator<Map.Entry<Integer, String>> it = set.iterator();
it.hasNext();) {
// send out notification...
}

// in another class, after check and handle the notification. If
failed, should not release resource.
void release(int key) {
Map<Integer, String> sessions =
((MockManagedPlugin)plugin).getSessions();
sessions.remove(key);
}

Do you have any suggestion to me about this issue?
 
B

Bill David

I have thought out a solution:
clone the map and then iterate the cloned map instead of the original
map.

But I hope there is some better solution.
 
O

Owen Jacobson

I have thought out a solution:
clone the map and then iterate the cloned map instead of the original
map.

But I hope there is some better solution.

You need to restructure your logic so that the code processing the
element is not also responsible for removing the element. One
possibility would be having it return a flag indicating whether the
element should be removed or not:

public boolean doSomethingWithElement (Integer value) {
return (value % 2 == 0);
}

public void doSomethingToAllElements () {
for (
Iterator<Map.Entry<String, Integer>> iter =
this.someMap.entrySet ().iterator ();
iter.hasNext ();
) {
Map.Entry<String, Integer> entry = iter.next ();
if (doSomethingWithElement (entry.getValue ())
iter.remove ();
}
}
 
L

Lew

Bill said:
I have thought out a solution:
clone the map and then iterate the cloned map instead of the original
map.

But I hope there is some better solution.

How about you "ensure the other class has completely handled the notification"
up to, but *not* including removal of the item. Have it return (if from
another thread, in the usual thread-safe way) to the loop with the iterator,
and have the iterator remove the item.

The whole point of the iterator throwing ConcurrentModificationException is
that you must not conncurrently modify the structure and expect the iterator
to function correctly. Since the iterator needs to know its place anyway, let
the iterator handle the remove as it's supposed to.
 
M

Mike Schilling

Bill David said:
In my application, I will iterate a map and send out a message to
another class with the key of current object to which iterator is
pointing. And the operation is in the same thread. This class get
the
notification will do more handle and then, it will try to remove
this
object from the map with the key.

Why does the called class even know about the existence of the map?
Pass it an object it can call back when it's completely handled the
notification, and let the callback use the iterator to remove the map
entry.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top