0

I am trying to understand the below examples and how they behave and they do not really behave as I expect.

Example 1:

Map<Long, String> xx = new HashMap<>();
xx.put(1L, "1");
//Does not throw exception even if the map during iteration is changed
for (String value : xx.values()) {
    xx.put(Long.parseLong(value) +1 , value + 1);
}

Example 2:

Map<Long, String> xx = new HashMap<>();
xx.put(1L, "1");
// same operation in stream api and throws exception
xx.values()
        .forEach(value -> {
            xx.put( Long.parseLong(value) + 1, value + 1);
        });

Example 3:

Map<Long, String> xx = new HashMap<>();
xx.put(1L, "1");
// stream api this time does not put a new value but updates the former value and does not throw Concurrent Modification exception
xx.values()
        .forEach(value -> {
            xx.put( Long.parseLong(value), value + 1);
        });

I was pretty confused with the behavior here. I expect first one to throw exception because collection is changed during iteration. I would expect second and third example to throw exception. After seeing the first example, I would expect the second example not to throw exception because it is basically the same thing with first one and first one does not throw exception. I would expect third example to throw exception but it does not.

Can someone explain what is happening here?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Fatih Arslan
  • 1,054
  • 1
  • 9
  • 10
  • 2
    For `HashMap`, CMEs don't *have* be thrown. It's on a "best-effort basis". As soon as you start iterating and modifying at the same time, the behaviour is undefined. – Sweeper May 04 '23 at 08:11
  • See [this answer of mine](https://stackoverflow.com/a/73341695/5133585), the [documentation for `HashMap`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/HashMap.html) has a similar paragraph as the one quoted in that asnwer. While you're at that page, check out the documentation for `values()`, which is also relevant. – Sweeper May 04 '23 at 08:13
  • 1
    Add more than one value to the map (e.g. `xx.put(3L, "2");`), and all of them will produce an exception; on the other hand, if you add `xx.put(2L, "2");` the first and third example will also not produce an exception). This has to do with the mechanism used to detect the modification. – Mark Rotteveel May 04 '23 at 08:37
  • 1
    Usually, `forEach` methods can detect changes more reliably because the operation has a well-defined end (when `forEach` returns), so there can be a final check. In contrast, the for-each loop uses an `Iterator` which has no mechanism to tell the implementation when the iteration is over. The caller may abandon the iterator at any time. • But updating an existing key is not a structural modification at all. And, by the way, calling `forEach` on a `Collection` is not using the `Stream` API. – Holger May 05 '23 at 06:59

0 Answers0