Search code examples
javaconcurrencyjava.util.concurrentconcurrenthashmap

Removing from a ConcurrentHashMap


I am trying to remove certain entries from a ConcurrentHashMap. However since this happens in a multithreaded environment, entries may be removed and/or modified while the iteration is in progress. When this happens the remove method on the iterator will remove the entry even if it was modified since received through next. I have constructed an example program to illustrate this:

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

map.put("foo", "bar");
map.put("quz", "qaz");

CountDownLatch foundBar = new CountDownLatch(1);
CountDownLatch doneModifying = new CountDownLatch(1);
CountDownLatch doneIterating = new CountDownLatch(1);

new Thread(() -> {
    try {
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            if (entry.getValue().equals("bar")) {
                foundBar.countDown();
                doneModifying.await();
                it.remove();
            }
        }
        doneIterating.countDown();
    } catch (InterruptedException e) {
        throw new Error(e);
    }
}).start();

foundBar.await();
map.put("foo", "nob");
doneModifying.countDown();

doneIterating.await();
System.out.println(map);

The output will be {quz=qaz} and not as I expected {quz=qaz,foo=nob}. My question is: How do I achieve the desired behavior? Is the remove(key, value) method on the Map during the iteration the right choice?


Solution

  • Yes, you should use the two argument remove method. While direct collection mutation during iteration is generally a bad thing for most java collections, ConcurrentHashMap allows this.