0

I have a simple HashMap algorithm:

HashMap<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);

Iterator<Integer> it = map.keySet().iterator();
while(it.hasNext()) {
    Integer key = it.next();
    if (key.equals(2)) {
        map.put(1, 2);
    }
}

and this is working fine. But when I will modify the condition body to:

if (key.equals(2)) {
    map.put(0, 2); // changed index '1' to '0'
}

it's always throwing java.util.ConcurrentModificationException. The same is happening for key values lesser than 0.

What am I missing?


Edit

Seems that if I will remove the third Map element:

map.put(1, 1);
map.put(2, 2);
// map.put(3, 3);

it's working fine

m.antkowicz
  • 13,268
  • 18
  • 37
  • A `ConcurrentModificationException` occurs when you modify a collection you are currently iterating over. The iterator is not being notified of the change on the underlying collection. Create a copy of the collection to iterate over, to avoid this. The first one is working fine, since you don't change the size of the collection. – maloomeister Dec 06 '21 at 08:49
  • Does this answer your question? [Why is a ConcurrentModificationException thrown and how to debug it](https://stackoverflow.com/questions/602636/why-is-a-concurrentmodificationexception-thrown-and-how-to-debug-it) – maloomeister Dec 06 '21 at 08:50
  • but `map.put(1, 2);` is working fine - I would understand if the exception would be thrown everytime but why it's happening only for `<=0` keys? – m.antkowicz Dec 06 '21 at 08:50
  • put(100000,x) will do the same – Antoniossss Dec 06 '21 at 08:52
  • In this particular implementation of hasmap iterator, size of the collection is what matters, not actual key-value pairs – Antoniossss Dec 06 '21 at 08:52

3 Answers3

2

You are changing the size of the map during iteration which causes the exception. Keep in mind that it is not put operation that throws the exception but an attempt to get next element via iterator via Iterator#next

In your case, if you have "extended" the map, iterator will throw exception on next(). However if it was done on the last iteration, hasNext returns false. This will lead to skipping next() call and will not throw an exception.

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • so why `map.put(1, 2);` is working fine? – m.antkowicz Dec 06 '21 at 08:49
  • Because you are not changing the size of the map. YOu start with X elements and after put its still X elements - you replaced existing key with new value. put (0,x) adds new element thus changing size of the map. Any other `put(notYetInTheMap,x)` will cause this as well as removal of the element – Antoniossss Dec 06 '21 at 08:51
  • this makes sense but then why is this working when I have elements at `1` and `2` and then do `map.put(0, 2)`? (please see my edit) – m.antkowicz Dec 06 '21 at 08:52
  • Again, you replace element and the map is still of size 2. **Map size is what matters here** not its content – Antoniossss Dec 06 '21 at 08:53
  • I do not - I don't have element with `key = 0` – m.antkowicz Dec 06 '21 at 08:53
  • 2
    @m.antkowicz then it's fine, since it's the last iteration of the iterator anyways, there is no more `hasNext()` checks. – maloomeister Dec 06 '21 at 08:54
  • I'm so sorry @Antioniossss I missed button in the comment - I meant `map.put(0,2)` – m.antkowicz Dec 06 '21 at 08:54
  • 1
    As I wrote, exception is thrown by the iterator during next() call, so no call == no exception. – Antoniossss Dec 06 '21 at 08:55
  • Ok guys you are right! It's because it was the last run of the `while` loop - when I'm changing condition to `if(key.equals(1))` I have again the exception. Thank you so much I was going crazy! – m.antkowicz Dec 06 '21 at 08:56
1

It's not related only for keys lesser than 0.

First of all it works with map.put(1, 2); because here you not add new item to map, you just replace existing value to another.

But if you try to add any new item with new index (such as 0 in your example, but there can be any new index, for example 4,5,6,....) you always get java.util.ConcurrentModificationException.

So you can't add new items inside the iterator.

1

Refer to the Javadoc (emphasis added):

The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Structural modification is defined earlier as:

A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.

So:

  • map.put(1, 2); is not a structural modification, because the key 1 is already in the map. All you are doing is changing the value it is associated with. No ConcurrentModificationException is thrown.
  • map.put(0, 2); is a structural modification, because the key 0 is not already in the map. As such, a ConcurrentModificationException is thrown.
Andy Turner
  • 137,514
  • 11
  • 162
  • 243