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?
Bill David - 21 Dec 2007 02:52 GMT
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.
Owen Jacobson - 21 Dec 2007 03:00 GMT
> 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 ();
}
}
Lew - 21 Dec 2007 03:44 GMT
> 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.

Signature
Lew
Mike Schilling - 21 Dec 2007 07:20 GMT
> 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
[quoted text clipped - 3 lines]
> 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.