Adding elements to an HashMap concurrently

If I iterate over a standard HashMap and attempt to add elements to it while iterating I get ConcurrentModificationException.

So I try an HashMap allowing concurrent adds:

ConcurrentHashMap<String, Integer> cMap = new ConcurrentHashMap<>();
cMap.put("one", 1);
cMap.forEach((key, value) -> cMap.put(key + key, value + value));
System.out.println(cMap);

However, the resulting map is a bit weird:

 {oneoneoneoneoneoneoneone=8, one=1, oneone=2, oneoneoneone=4}

and if changing the key to zx (cMap.put("zx", 1)), the result is now:

{zxzx=2, zx=1}

Questions:

1) Why this happens? The two concurrent operations (iterating and adding) should not conflict.

2) How to fix the inconsistency?

As opposite to Collections, when changing a String while iterating over the chars of that String, this issue is not being met:

        String str = scanner.next();
        for (int i = 1; i < str.length(); i++) {
            if (str.charAt(i) == str.charAt(i-1)) {
                str = str.substring(0, i-1) + str.substring(i+1);
                i = 0;
            }
        }
        if (str.length() == 0) {
            System.out.println("Empty String");
        } else {
            System.out.println (str);
        }
    }

note that in the above loop, the source String isn't actually changed but re-assigned because String is immutable and can not be modified.

The above code works fine and consistently. Is this an example of why Strings are thread safe?

Upvotes: 0

Views: 698

Answers (1)

Joe C
Joe C

Reputation: 15694

It comes down to which hash bucket your new elements get added. In your first example, your new elements get added to a later hash bucket than the one you're working on, and which the iterator has yet to reach. In your second example, your new elements get added to an earlier hash bucket, which the iterator has already read.

You should be very wary about modifying collections in the middle of an iteration. In your case, it might be better for you to add your new entries into a new map, and then merge them together.

Upvotes: 3

Related Questions