Search code examples
javalistloopsconcurrentmodificationexception

Iterate through list and add to list without incurring ConcurrentModificationException - Java


Apologies, this has been done to death but I am really struggling to implement a solution to this problem and I am quite new to Java.

I need to be able to call a method which basically allows a Person object to be added to a list.

The main problem I have encountered whilst trying to implement a solution is a 'ConcurrentModificationException' which is understandable given I have been trying to update the list whilst in the middle of a for each loop.

So, I have come up with the following solution which prevents the 'ConcurrentModificationException' but my solution doesn't work properly and seems overcomplicated - see following code snippet of method:

public void addPerson(Person aPerson) {
    // tempPersons list for temporarily storing Person objects
    tempPersons = new ArrayList<>();

    // if persons list is initially empty, add aPerson object to it
    if (persons.isEmpty()) {
        persons.add(aPerson);
    }
    // if persons list is not initially empty
    if (!persons.isEmpty()) {
        // loop through persons list
        for (Person anotherPerson : persons) {
            // if persons list anotherPerson first name is the same as the aPerson first name, print message
            if (anotherPerson.getFirstName().equals(aPerson.getFirstName())) {
                System.out.println("The Person " + aPerson.getFirstName() +
                        " is already entered in the list");
            }
            // otherwise add the aPerson object to the tempPersons list
            else {
                tempPersons.add(aPerson);
            }
        }
        // once out of loop, add the tempPersons list to the persons list
        persons.addAll(tempPersons);
        // create new tempPersons2 list based on a hashset of the persons list so there are no duplicates
        List<Person> tempPersons2 = new ArrayList<>(
                new HashSet<>(persons));
        // assign tempPersons2 list without duplicates to persons list
        persons = tempPersons2;
    }
}

So, if for example I call the above addPerson method 4 separate times with a mixture of unique and duplicate objects (aPerson param), the method correctly identifies that there is already an object with the same first name in there but the persons list always seems to end up with a duplicate object (first name) in there e.g. if I have the following objects:

Person1 FirstName = Bob

Person2 FirstName = Jan

Person3 FirsName = Bob

Person4 FirstName = Ann

Then I make the following method call 4 separate times:

addPerson(Person1);

addPerson(Person2);

addPerson(Person3);

addPerson(Person4);

When I call a print method, I get the following output:

The Person Bob is already entered in the list

Jan

Ann

Bob

Bob

I would expect the following:

The Person Bob is already present

Jan

Ann

Bob

Apologies for all the waffle and what is probably a really simple solution to most of you but I have been stuck on this for a couple of days.

Similar article can be found here but I am still struggling.

Adding elements to a collection during iteration


Solution

  • Without changing your code too much:

      public void addPerson(Person person) {
        // The guard is more or less a premature optimization and should be removed.
        if (persons.isEmpty()) {
          persons.add(person);
          return;
        }
    
        for (Person anotherPerson : persons) {
          if (anotherPerson.getFirstName().equals(person.getFirstName())) {
            System.out.println("The Person " + person.getFirstName() +
                " is already entered in the list");
            return;
          }
        }
        persons.add(person);
      }
    

    This would exit the method when a match is found, if there are no matches the person is simply added after the loop. Note the return in the first check.

    Optimizations of this code could be using a Map or Set to speed up the contains check; also just using anyMatch on persons would lead to a more elegant solution.

    The duplicates are caused by your for loop and it's else condition. If Bob and Jan is in your collection and you add a second Bob then Jan won't be equal to Bob and your else path is executed adding a duplicate to your final persons List.