안녕하세요, 하원입니다.
오늘은 제가 겪었던 ConcurrentModificationException 오류에 대해 소개해 보겠습니다.
이 오류는 알고리즘 문제를 푸는 도중에 발생했는데요, 자세히 살펴보겠습니다.
문제 상황
List<String> words = new ArrayList<>();
words.add("A");
words.add("B");
words.add("C");
words.add("D");
for (String word : words) {
if (word.equals("A")) {
words.remove(word);
}
}
위 코드는 String 타입 List에 A, B, C, D라는 값을 추가한 이후, List를 순회하면서 A라는 값을 찾을 때 List에서 삭제하는 간단한 코드입니다.
실행하면 어떤 결과가 나올까요?

위 사진과 같이 ConcurrentModificationException이 발생하게 됩니다.
근데 신기한 점은 A, B, D를 삭제하면 오류가 발생하고, C를 삭제하면 정상적으로 동작합니다.
왜 이런 상황이 발생한 걸까요?
원인
결과적으로는 remove 동작 과정에서 modCount 변수의 불일치 때문에 발생한 것입니다.
단계적으로 설명해 보겠습니다. 먼저 modCount 변수가 무엇인지 설명하겠습니다.
modCount 변수
- ArrayList, LinkedList 등과 같은 리스트 내부에서 새로운 요소가 추가되거나 삭제될 때 modCount라는 변수가 증가합니다.
- 즉, modCount는 리스트가 변경되었음을 감지할 수 있게 해주는 변수입니다.
오류 발생 과정
remove()가 호출되면, ArrayList 내부에 있던 modCount 변수가 증가하게 됩니다.next() 호출) 초기에 저장한 modCount와 현재 modCount 값을 비교합니다.ConcurrentModificationException을 발생시킵니다.Iterator 살펴보기
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
next()와 checkForComodification() 함수입니다.next() 코드를 살펴보면, 첫 번째 줄에 checkForComodification()이 실행되는 것을 볼 수 있습니다.checkForComodification()
- modCount 변수를 비교하는 함수입니다.
expectedModCount: Iterator가 생성될 때 초기에 저장한 modCount 값modCount: 리스트가 변경되었을 때마다 증가하는 값- 위 2개의 변수가 불일치하면,
ConcurrentModificationException을 발생시키게 됩니다.
해결 방법
for (String word : words) {
if (word.equals("A")) {
words.remove(word);
break;
}
}
break문을 통해 for-each문을 종료시켜 버리는 겁니다.Iterator<String> iterator = words.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if ("A".equals(str)) {
iterator.remove();
}
}
iterator.remove() 코드는 내부적으로 expectedModCount 변수를 함께 업데이트 하기 때문에words.removeIf(str -> "A".equals(str));
removeIf() 함수입니다.removeIf()는 내부적으로 modCount를 직접 업데이트 하기 때문에 예외가 발생하지 않습니다.기본기의 중요성
사실 코딩 테스트를 진행하던 도중 ConcurrentModificationException 오류가 발생해버렸습니다. 검색도 불가능해서 해결 방안을 찾지 못했고, 결국 코드를 완성하지 못한 채 제출하게 되었습니다. 문제를 몰라서 못 푼 것보다 이러한 프로그래밍 언어의 미숙으로 인해 못 푼 것이 더 속상한 것 같습니다. 프로젝트를 통해 경험을 쌓는 것도 중요하지만, 자바와 같은 기본기를 더 깊이 있게 다룰 줄 아는 것도 꽤나 중요하다는 것을 느끼게 되었습니다.