Alessandra Maria
Alessandra Maria

Reputation: 255

ConcurrentModiciationException when adding an element to list while iterating

I have this code in my adapter which displays a list of schools. There is also a search filter. Whenever the search filter has a value the list must show schools that contains the letters of search filter. Here is my code -

public boolean refreshAdapter() {
        List<School> schools = cache().getSchools();
        if (schoolList != null && !schoolList.isEmpty()) {
            schoolList .clear();
        }

        schoolList = schools;

        for (School school: schoolList) {

            String name = school.getName();
            String address = vehicle.getAddress();

            if (name.contains(searchFilterText)|| address.contains(searchFilterText)) {
                schoolList.add(school);
            }
        }
        notifyDataSetChanged();
        if (schoolList!= null) {
            return schoolList.isEmpty();
        }
        return false;
    }

How can I get this code running without concurrent modification exception? I tried using ListIterator but did not help me

Upvotes: 0

Views: 47

Answers (3)

Dorian Gray
Dorian Gray

Reputation: 2981

Note that this Exception occurs because you must not add elements to the collection while iterating it. Did you get confused with schools and shoolList? Giving them more reasonable names can help a lot!

To do it correctly, you have to build a new filtered list (I commented the lines I changed)

    // this is the new filtered list
    schoolList = new ArrayList<>();

    // iterate the original list
    for (School school: schools) {

        String name = school.getName();
        String address = vehicle.getAddress();

        if (name.contains(searchFilterText)|| address.contains(searchFilterText)) {
             // add it to filtered list
            schoolList.add(school);
        }
    }

Upvotes: 0

Justin Albano
Justin Albano

Reputation: 3949

The ConcurrentModificationException originates because the schoolList list is being modified within the process of iterating through that list. An alternative would be iterate the schools list instead and make the additions to the schoolList, which separates the list being iterated from the one being modified:

schoolList = new ArrayList<>();

for (School school: schools) {

    String name = school.getName();
    String address = vehicle.getAddress();

    if (name.contains(searchFilterText)|| address.contains(searchFilterText)) {
        schoolList.add(school);
    }
}

Another alternative (using JDK 8 or above) is to filter the schools list and capture the result into schoolList:

schoolList = schools.stream()
    .filter(school -> school.getName().contains(searchFilterText) || school.getAddress().contains(searchFilterText))
    .collect(Collectors.toList());

Based on the current state of the code, it appears that you will be re-adding elements to the schoolList, because it already contains those elements, hence why you are iterating over them. The code examples above result in lists that only contain the matching elements. If it desired to have the matching elements re-added to the schoolList, as in the original question, you can replace the following line in the first example

schoolList = new ArrayList<>();

with

schoolList = new ArrayList<>(school);

This will create a copy of the school list and store it in schoolList before the iteration begins.

Be careful if this is the intended behavior, as it may cause an infinite loop. For example, in the original question, had a ConcurrentModificationException not been thrown, if you add a School object that matches the condition (name.contains(searchFilterText)|| address.contains(searchFilterText)), it will also be iterated later on, which in turn would match the condition, and be re-added. This process would repeat infinitely. For example, suppose the only element in the schoolList matches the condition, the following would be the results contained in schoolList after each iteration:

Iteration 0:

matching school A    <-- Current element of iteration
matching school A    <-- [newly added element]

Iteration 1:

matching school A
matching school A    <-- Current element of iteration
matching school A    <-- [newly added element]

Iteration 2:

matching school A
matching school A
matching school A    <-- Current element of iteration
matching school A    <-- [newly added element]

And so on, infinitely.

Upvotes: 2

Tamas Rev
Tamas Rev

Reputation: 7166

You are getting ConcurrentModificationException-s because you are iterating through a list while adding things to it. This is, in general, prohibited because adding stuff through iteration is confusing. In some cases, the programmers want the iteration to iterate through the recently added item, sometimes not. So java forces us to do either of these things explicitly.

You can fix it with a temporary list that stores the items you want to add to the list.

Step 1: find the items you want to add.

List<School> tempSchoolList = new ArrayList<>(); // this is the new list

// most of the things below comes from the original code
schoolList = schools;

for (School school: schoolList) {

    String name = school.getName();
    String address = vehicle.getAddress();
    if (name.contains(searchFilterText)|| address.contains(searchFilterText)) {
        tempSchoolList.add(school); // add the new list to the temp school list
    }
}

Step 2: add the items from the temp list to the original

schoolList.addAll(tempSchoolList);

Upvotes: 0

Related Questions