2011년 9월 21일 수요일

ConcurrentModificationException 은 multi thread 를 쓰지 않아도 나온다.

아래와 같은 java 코드에서 ConcurrentModificationException 이 발생한다.


ArrayList<A> objects = new ArrayList<A>;objects.add(new A(1)); 
objects.add(new A(2)); 
objects.add(new A(3)); 
objects.add(new A(4)); 

for(A object : objects) { 
    if (object.equals(A(1))) { 
        objects.remove(object); 
    } 
}


Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:782)
at java.util.ArrayList$Itr.next(ArrayList.java:754)
at HelloWorld.main(HelloWorld.java:12)

exception 이름을 보면 다중 thread 를 사용하거나 할 때 생길 법한데, 왜 나올까?

결론부터 말하자면, iterating 을 하는 와중에 remove() 를 했기 때문이다.

위와 같이 사용한 for 구문을 foreach 구문이라고도 하는데, foreach 구문은 내부적으로 iterator 를 사용한다. 때문에, 위의 for 문은,

for(Iterator iterator = objects.iterator(); iterator.hasNext();) { 
    A object = (A)iterator.next(); 
    ....

와 동일하다.
그런데, iterator 는 next() 또는 remove() 메소드가 호출될 때 현시점의 collection 수정 횟수와  생성 시점의 collection 수정 횟수를 비교해보고, 다르다면 ConcurrentModificationException 을 뱉는다.

문제는, ArrayList 의 remove() 메소드는 collection 수정 횟수를 하나 증가시킨다.
때문에, objects.remove() 가 수행된 다음 loop 의 iterator.next() 호출 시에, collection 수정 횟수가 맞지 않아 ConcurrentModificationException 이 발생한다.


결국, iterator 를 이용한 순회중 remove 를 할 것이라면 바로 break 를 해서 루프를 빠져나오거나, foreach 구문을 쓰지 않는 게 좋겠다.




참고 사이트 : http://suein1209.tistory.com/323