0

Some background to the problem: I have a class which is responsible for informing various clients about changes in service. So it the class has several HashMaps of listeners and calls their methods by iterating those HashMaps when needed (it also has methods to add/remove listeners from those HashMaps). The problem is that often I need remove certain listeners while they are being iterated... and I need to be able to call remove(...) method from outside the loop. Both iterating and removing are happening on the same thread. Example of the class:

class SomeClass {
    Map<Listener, Boolean> listeners = new HashMap<Listener, Boolean>();

    void add(Listener listener) {
        listeners.put(listener, true);
    }

    void remove(Listener listener) {
        listeners.remove(listener);
    }

    void notifyListeners() {
        Iterator<Map.Entry<Listener, Boolean>> it = listeners.entrySet().iterator();
        while (it.hasNext()) {
            it.next().getKey().onDo();
        }
    }

    static interface Listener{
        void onDo();
    }}

And the way I want to call remove(...) (and how that fails):

final SomeClass someClass = new SomeClass();

    Listener a = new Listener(){
        @Override
        public void onDo() {
            someClass.remove(this);
        }
    };

    Listener b = new Listener(){
        @Override
        public void onDo() {
            someClass.remove(this);
        }
    };

    Listener c = new Listener(){
        @Override
        public void onDo() {
            someClass.remove(this);
        }
    };

    someClass.add(a);
    someClass.add(b);
    someClass.add(c);

    someClass.notifyListeners();
    // java.util.ConcurrentModificationException

Is there any trick to do this?

Maybe Julius
  • 1,203
  • 2
  • 13
  • 16

2 Answers2

2

Did you tried ConcurrentHashMap?

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html

finejustice
  • 413
  • 2
  • 9
1

Your problem is candidate for using ConcurrentHashMap. Just replace your map definition to ConcurrentHashMap

Map<Listener, Boolean> listeners = new ConcurrentHashMap<Listener, Boolean>();
vineet
  • 336
  • 2
  • 11
  • yeay, it's working. By the way, when is the removal taking place? Can I be sure that entry **a** was removed when the call returns? – Maybe Julius Jun 12 '13 at 18:50
  • Remove happens real time, however iteration happens over a snapshot of collection. Read documentation shared by @finejustice. It will help :) – vineet Jun 13 '13 at 03:08