오늘 Java로 MUD 게임을 개발하던 중 ConcurrentModificationException 이라는 예외를 만나게 되었다.
지금까지 개발하면서 이 예외를 처음 본 것 같다는 생각이 들었다 .
이전에 본 적이 있다면 조금이라도 기억이 날텐데 이 예외는 왠지 오늘 처음 발생한 예외라고 생각했다.
자료구조를 순회하면서 해서는 안되는 동작에 대해 알고 있다면 발생하지 않는 예외에 속한다.
왜 발생하는 지와 그 해결 방법에 대해서 간단하게 알아보고자 한다.
핵심은 간단하다.
자료구조를 순회하면서 우리는 특정 원소에 대해 Delete 연산을 수행하게 되는데 이때 자료구조를 for loop으로 순회하면서 .size() 혹은 len() 혹은 .legth를 통해 loop 횟수를 정해줬을 것이다. 그런데 순회하는 도중에 삭제해버리면 그 사이즈가 순회 도중 줄어들기 때문에 마지막에 가서는 존재하지 않는 요소에 대해 접근하려는 행위와 마찬가지가 된다.
이러한 동작을 막고자 예외를 발생시키는데 그게 바로 ConcurrentModificationException 이다.
참고로 이러한 예외의 해결 방법에는 여러 가지가 있다. 참고로 가장 좋은 방법은 그냥 순회 도중에 아이템을 삭제하지 않도록 만드는 것이 근본적인 문제를 해결할 수 있어서 가장 좋다.
해결 방법은 크게 네 가지가 있다.
1. for loop에서 역순으로 순회하면서 삭제
이 방법은 ArrayList와 같은 자료구조에서만 사용할 수 있는 방법이다. 왜냐면 역순으로 접근하면 요소를 삭제해봤자 결국에는 2, 1, 0 순으로 접근하기 때문에 영향을 받지 않기 때문이다. 다만 이렇게 짠 이유를 모르는 팀원이 리팩토링을 할 수있기 때문에 별로 좋은 방법은 아니라고 한다.
2. for loop 에서 삭제할 요소를 찾고 removeAll을 통해 삭제
삭제할 요소들을 순회하는 과정에서 모두 담아내고 그걸 나중에 순회가 끝난 다음에 removeAll을 통해 한꺼번에 컬렉션에서 삭제해주면 된다. 이 방법은 순회가 끝나고 진행되기 때문에 전혀 문제가 발생하지 않는다. 다만 개인적으로 생각하기에 삭제할 요소를 담기 위한 자료구조를 하나 더 사용해야 하기 때문에 이 부분이 낭비라는 생각이 든다.
3. Interator를 활용한 순회 및 삭제
Interator는 순회 도중 요소를 삭제해도 ConcurrentModificationException이 발생하지 않도록 설계되어있다고 한다. 또한 예외가 발생하지 않을 뿐더러 안전하다고 한다.
4. removeIf() 로 요소 삭제
자바8에서 도입된 기능으로 removeIf에 어떤 요소를 삭제할 지에 대한 람다를 전달하면 된다고 한다.
개인적으로 위에서 언급된 방법들 중에서 4번이 가장 깔끔하게 코드가 나온다고 생각된다. 3번은 Iterator를 사용하고 어찌 되었든 loop가 코드 상으로 눈에 보인다. 하지만 4번은 그냥 removeIf의 인자로 람다식을 전달하기만 하면 되서 딱 한 줄의 코드가 작성된다. 그리고 1번은 제일 별로라고 생각된다. 그리고 2번은 메모리 낭비가 있다.
굳이 쓸거면 4번을 쓰자.
아래 블로그에서 많은 도움을 받았습니다.
감사합니다.
https://codechacha.com/ko/java-concurrentmodificationexception/
'Java' 카테고리의 다른 글
[Java] Java의 참조 (0) | 2023.01.01 |
---|---|
[Java] 외부에서 스레드를 안전하게 종료하는 방법 (0) | 2022.11.30 |
[Java] 생성자에서는 getInstance호출에 신중하자. (0) | 2022.11.30 |
[Java] Java에서 Redis 를 사용해보자. Jedis 사용하기 (0) | 2022.11.23 |
[Java] PrintWriter 의 AutoFlush 사용 (0) | 2022.11.23 |