10

Im playing around with some code for my college course and changed a method from

public boolean removeStudent(String studentName)
{
    int index = 0;
    for (Student student : students)
    {
        if (studentName.equalsIgnoreCasee(student.getName()))
        {
            students.remove(index);
            return true;
        }
        index++;
    }
    return false;
}

To:

public void removeStudent(String studentName) throws StudentNotFoundException
{
    int index = 0;
    for (Student student : students)
    {
        if (studentName.equalsIgnoreCase(student.getName()))
        {
            students.remove(index);
        }
        index++;
    }
    throw new  StudentNotFoundException( "No such student " + studentName);
}

But the new method keeps giving a Concurrent Modification error. How can I get round this and why is it happening?

ToniHopkins
  • 369
  • 3
  • 11
  • 30
  • possible duplicate of [Behavior of ConcurrentModificationException](http://stackoverflow.com/questions/6060913/behavior-of-concurrentmodificationexception) – Marko Topolnik Mar 13 '13 at 11:51
  • 1
    This is one of three most-asked Java questions. You must use an iterator and call `iterator.remove`. – Marko Topolnik Mar 13 '13 at 11:52
  • you are changing the list while iterating over it, java collections don't allow this. full explanation can be found [here](http://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html) – Oren Mar 13 '13 at 11:53

8 Answers8

21

It is because you continue traversing the list after performing remove().

You're reading and writing to the list at the same time, which breaks the contract of the iterator underlying the foreach loop.

Use Iterator.remove()

for(Iterator<Student> iter = students.iterator(); iter.hasNext(); ) {
    Student student = iter.next();
    if(studentName.equalsIgnoreCase(student.getName()) {
        iter.remove();
    }
}

It is described as the following:

Returns the next element in the iteration.

Throws NoSuchElementException if the iteration has no more elements.

You can use Iterator.hasNext() to check if there is a next element available.

Community
  • 1
  • 1
Zutty
  • 5,357
  • 26
  • 31
2

foreach construct uses an underlying Iterator.

In the second method you continue to iterate even after removing an item from the list. This is resulting in the exception that you see. Take a look at this statement taken from ConcurrentModificationException documentation:

For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances. Some Iterator implementations (including those of all the general purpose collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected.

1

You are not allowed to remove an element from your collection while you iterate over it. The iterator detects a structural change during its usage, and throws the exception. Many collections are implemented in such a way.

Use the iterator directly instead:

    Iterator<Student> it = students.iterator();
    while (it.hasNext()) {
        Student student = it.next();

        if (studentName.equalsIgnoreCase(student.getName())) {
                it.remove();
                return true;
        }
    }
    return false;
Eyal Schneider
  • 22,166
  • 5
  • 47
  • 78
1

you can avoid concurrent modification error buy just breaking the loop after removing the element or if the method has a return type return a value after removing the element.

Ker p pag
  • 1,568
  • 12
  • 25
0

This error occurs because you are trying to alter the size of a collection while you are iterating it. If you have 10 students, you start your loop expecting to go through 10 iterations. When you remove a student, how many iterations do still need to go? The answer obviously depends on where you removed your student from the list and where you currently are in your iteation. Obviously, java cannot know this.

To get around this, you must use an iterator. You can accomplish this as follows:

Iterator<Student> studentsIterator;
for(studentsIterator = students.iterator(); studentsIterator.hasNext();)
{
    Student student = studentsIterator.next();
    if(student... /* condition */)
    {
        studentIterator.remove();  //This removes student from the collection safely
    }
}
Martin
  • 830
  • 1
  • 7
  • 24
0

You are not allowed to remove an element from students collection while iterating through it.

This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.

For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances.

http://docs.oracle.com/javase/6/docs/api/java/util/ConcurrentModificationException.html

Try changing to

Iterator<Student> itr = students.iterator();
while (itr.hasNext()) {
    Student student = itr.next();
    if (studentName.equalsIgnoreCase(student.getName()))
    {
        itr.remove();
    }
}
Community
  • 1
  • 1
Nikolay Kuznetsov
  • 9,467
  • 12
  • 55
  • 101
0

If you want to remove inside a loop you should use an iterator and its remove method

public boolean removeStudent(String studentName)
{
    Iterator<Student> itS = students.iterator();
    while(itS.hasNext())
    {
        Student student = itS.next();
        if (studentName.equalsIgnoreCasee(student.getName()))
        {
            itS.remove();
            return true;
        }
    }
    return false;
}
A4L
  • 17,353
  • 6
  • 49
  • 70
0

You shouldn't delete objects from a collection while using a for-each statement - this will cause exceptions as your iterator faces a changed collection in the course of its iterations. (the for loop) either use a regular for loop (for int i = 0; i < 100; i++) etc... or keep the objects to remove in a list, and remove them outside of the for loop.

Also, you remove the object by index where index is : 0 , 1 , 2 but index should actaully be the index of the student.

omer schleifer
  • 3,897
  • 5
  • 31
  • 42