0

I have a Map<String, ClassA> results.

When I do this, I get a ConcurrentModificationException:

results.entrySet().stream().map((entry) -> {
    ClassA objA = entry.getValue();
    if(objA.getList() != null) {
        objA.getList().forEach(x -> {
            if(x.getAttr() != null && x.getAttr.containsKey(key)) {
                List<String> y = x.getAttr().get(key);
                y.replaceAll(ClassB::transformationFunc);
            }
        });
    }
})

Basically what I am trying to do is if my results, has a value of ClassA, check for each element of the list if there is any Attribute with a given key. If so, replace the values of the key with a given transformation function.

public static String transformationFunc(String input) {
    try {
        JSONObject inputJson = (JSONObject) jsonParser.parse(input);
        return String.format("%s_%s", inputJson.get(key1), inputJson.get(key2));
    } catch (ParseException e) {
        //log
        //throw
    }
}
user1692342
  • 5,007
  • 11
  • 69
  • 128
  • 2
    Possible duplicate of [Java Concurrent Modification Exception Error](https://stackoverflow.com/questions/15384486/java-concurrent-modification-exception-error) – Raju Sharma Nov 07 '17 at 11:26
  • 2
    Could we see the code of `ClassB::transformationFunc` ? – Alexandre Dupriez Nov 07 '17 at 11:44
  • @AlexandreDupriez Updated question with transformationFunc – user1692342 Nov 07 '17 at 15:40
  • Interesting - assuming you are using a JDK implementation of `List`, `replaceAll` will flag your list as mutated after all its elements have been replaced. But I cannot see from your code where and when the co-modification occurs. Do you have the stack trace associated to the `ConcurrentModificationException`? – Alexandre Dupriez Nov 07 '17 at 16:08

1 Answers1

4

Basically, you get a ConcurrentModificationException whenever you modify a collection that you are iterating over.

The only way in which you are allowed to modify a collection that you are iterating over is by explicitly using an Iterator. If you want to do work on a collection while you're iterating over it, you have to do it on a working copy of the collection instead.

Piotr Wilkin
  • 3,446
  • 10
  • 18
  • Would I need to replace the stream as well? Or do I just need to modify the foreach to use an iterator? – user1692342 Nov 07 '17 at 11:30
  • Yes, using a stream is also implicitly iterating over a collection (same as using `forEach()` or a `foreach` loop). You have to stream over a copy of the map. – Piotr Wilkin Nov 07 '17 at 11:59
  • To clarify: you only have to make a copy if your code is actually modifying **the collection you're iterating over**. By "modifying the collection" I mean "adding, removing or replacing elements" - unless you use some sort of weird observable collection, modifying the internals of objects that are parts of a collection does not constitute modifying the collection. – Piotr Wilkin Nov 07 '17 at 12:39
  • @Eugene: that's obviously hard to answer without seeing the internals of the methods in question or seeing a stacktrace. – Piotr Wilkin Nov 07 '17 at 12:42
  • @Eugene I updated the question. Mine is just a string modification. Does this count as modifying the collection ? – user1692342 Nov 07 '17 at 15:41
  • Can you provide the stacktrace for the `ConcurrentModificationException`? – Piotr Wilkin Nov 07 '17 at 15:52
  • 1
    @user1692342 it's not. your problem is elsewhere or you are not showing us everything. **stacktrace....** also that exception would be thrown when you change the size of the collection you are streaming from, not the contents. – Eugene Nov 07 '17 at 20:43