0

I wrote the following code:

for (Character currentChar : userDocuments.keySet()) {
    List<QueryDocumentSnapshot> currentList = userDocuments.get(currentChar);
    if (currentList == null) {
        userDocuments.remove(currentChar);
        continue;
    }
    for (int index = 0; index < currentList.size(); ++index) {
        final String currentFullName = currentList.get(index).getString("full_name");
        if (currentFullName == null || !(searchText.contains(currentFullName))) {
            currentList.remove(index);
        }
    }
    if (currentList.size() == 0) {
        userDocuments.remove(currentChar);
    }
}

I want to iterate over a map Map<Character,List<QueryDocumentSnapshot>> check if the full_name (field of each QueryDocumentSnapshot) contains searchText and if it's not, remove this element from the list. In case list is empty, remove the entire list. But for some reason I get java.util.ConcurrentModificationException on the first line. Also, how can I use contains without case sensitive?

vesii
  • 2,760
  • 4
  • 25
  • 71

1 Answers1

2

The ConcurrentModificationException occurs when an object is tried to be modified concurrently when it is not permissible. This exception usually comes when one is working with Java Collection classes. For Example - It is not permissible for a thread to modify a Collection when some other thread is iterating over it.

In your case, it is arising coz you are trying to remove some elements from both the Map and ArrayList while iterating over them.

You can avoid it using:

    Iterator<Map.Entry<Character, List<QueryDocumentSnapshot>>> mapIterator = userDocuments.entrySet().iterator();
    while (mapIterator.hasNext())
    {
      Map.Entry<Character,List<QueryDocumentSnapshot>> entry = mapIterator.next();
      List<QueryDocumentSnapshot> currentList = entry.getValue();
      if (currentList == null) {
         mapIterator.remove();
         continue;
      }
      Iterator<QueryDocumentSnapshot> listIterator = currentList.iterator();

      while (listIterator.hasNext()) {
          final String currentFullName = listIterator.next().getString("full_name");
          if (currentFullName == null || !(searchText.contains(currentFullName))){
             listIterator.remove();
           }
      }
      if (currentList.size() == 0) {
        mapIterator.remove();
      }
    }   

And to answer your question 'how can I use contains without case sensitive' , you can simply use something like this:

searchText.toLowerCase().contains(currentFullName.toLowerCase())
Abhinaba Chakraborty
  • 3,488
  • 2
  • 16
  • 37
  • The ConcurrentModificationException has nothing to do with removing from the list. Removing from a list while iterating by index does not result in CME because there is nowhere for the state of "currently iterating" to be held by the list, in order to know it needs to throw the CME. – Andy Turner Jul 05 '20 at 11:32
  • 1
    That's not to say that iterating by index is *correct*: the indexed approach will skip over the element after the one removed. An overall simpler and more efficient approach is to use `removeIf`. – Andy Turner Jul 05 '20 at 11:41