Search code examples
javaexceptionlinkedhashmapconcurrentmodification

Why the concurrent modification comes here


The following code throws the concurrent modification exception

import java.util.*;

public class SampleTest
{
public static void main(String[] args) {

    LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
    map.put("value1","a");
    map.put("value2","b");
    map.put("value3","c");
    map.put("value4","d");
    map.put("value5","e");

    // sublists
    List<LinkedHashMap<String, String>> subList = new ArrayList<>();
    for(int i = 0; i<5; i++) {
        subList.add(map);
    }

    List<List<LinkedHashMap<String, String>>> mainList = new LinkedList<>();
    for(int i = 0; i<3; i++)
    {
        mainList.add(subList);
    }

    List<LinkedHashMap<String,String>> temp =  mainList.get(mainList.size() - 1);
    List<LinkedHashMap<String,String>> temp2 =  mainList.get(mainList.size() - 2);
    for(LinkedHashMap<String,String> map2 : temp) {
        temp2.add(map); // Exception Thrown Here......
    }
}
}

However i fixed the code by creating a new List and add the map and finally add the new list outside the loop in temp2

example,
    List<LinkedHashMap<String,String>> pp = new ArrayList<>();
    for(LinkedHashMap<String,String> map2 : temp) {
       pp.add(map);
    }
    temp2.addAll(pp);

I would like to know in detail why the concurrent happens in the earlier code.

Thanks.


Solution

  • This code:

    List<List<LinkedHashMap<String, String>>> mainList = new LinkedList<>();
    for(int i = 0; i<3; i++)
    {
        mainList.add(subList);
    }
    

    Is adding subList three times to mainList. When I say "three times" I mean the code is literally adding the same instance three times. You could modify the element at any valid index of mainList and you'd be modifying all the other elements because they're the same instance. See this question; it may help with this concept.

    As a consequence, this code:

    List<LinkedHashMap<String,String>> temp =  mainList.get(mainList.size() - 1);
    List<LinkedHashMap<String,String>> temp2 =  mainList.get(mainList.size() - 2);
    

    Grabs the same List from two different indices and assigns it to two different variables. In other words, temp == temp2 (reference equality) is true.

    You then iterate the List using the temp variable while at the same time you are adding elements to the List using the temp2 variable:

    for(LinkedHashMap<String,String> map2 : temp) {
        temp2.add(map); // Exception Thrown Here......
    }
    

    But again, temp and temp2 refer to the same List. Your code is basically doing the following:

    List<Object> list = ...; // create new list and fill it
    Object someObj = new Object();
    
    for (Object obj : list) { // iterating list
        list.add(someObj); // modifying list while it's being iterated
    }
    

    End result is you're iterating the List while attempting to modify it at the same time. This is not allowed by LinkedList (or really any of the standard implementations of Collection). From the documentation:

    The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the Iterator's own remove or add methods, 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.

    If you aren't aware, the for-each loop internally uses an Iterator when the target of the loop is an Iterable.