바쁜 분은 이것만
- 조건에 맞는 것 지우기:
list.removeIf(s -> s.equals("b")); - 반복하면서 지워야 하면
Iterator의it.remove() List.of(),Arrays.asList()로 만든 리스트는new ArrayList<>(...)로 감싼 뒤 지우기
ArrayList에서 특정 값을 지우려고 for-each 안에서 remove()를 부르면 이런 에러가 납니다.
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1104)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1058)
at Cme.main(Cme.java:5)
이 에러가 왜 나는지, 그리고 지우는 방법 다섯 가지를 실제로 실행해서 비교했습니다(OpenJDK 27). 예제는 모두 [a, b, b, c, b]에서 "b"를 전부 지우는 코드예요.
1. 에러가 나는 코드
List<String> list = new ArrayList<>(List.of("a", "b", "b", "c", "b"));
for (String s : list) {
if (s.equals("b")) list.remove(s);
}
for-each는 내부적으로 Iterator를 써서 하나씩 꺼냅니다. Iterator는 꺼낼 때마다 "내가 모르는 사이에 리스트가 바뀌었나?"를 확인하는데, list.remove()로 직접 지우면 그 검사에 걸려서 예외를 던져요. 에러 메시지의 checkForComodification이 바로 그 검사입니다.
예외를 잡고 리스트를 찍어 보면 첫 번째 b만 지워진 상태에서 멈춰 있어요.
[a, b, c, b]
2. 더 위험한 경우: 에러 없이 끝나 버린다
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
for (String s : list) {
if (s.equals("b")) list.remove(s);
}
System.out.println(list);
[a, c]
이번엔 에러가 안 납니다. 뒤에서 두 번째 원소를 지우면 리스트 크기가 줄면서 반복이 "끝났다"고 판단해 다음 검사를 하지 않기 때문이에요. 결과가 맞아 보여서 넘어갔다가, 데이터가 바뀌면 그때 에러가 납니다. 이 방식은 통과하더라도 쓰지 마세요.
3. 인덱스 for문으로 앞에서부터 지우면 하나를 건너뛴다
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("b")) list.remove(i);
}
[a, b, c]
에러는 안 나지만 b가 하나 남았습니다. 1번 자리의 b를 지우면 뒤의 b가 1번으로 당겨지는데, i는 이미 2로 넘어가서 그 b를 못 보는 거예요. 연속된 값이 있을 때 생기는 문제라 테스트에서 잘 안 잡힙니다.
4. 제대로 지우는 방법 세 가지
① removeIf (자바 8 이상, 가장 간단)
list.removeIf(s -> s.equals("b"));
[a, c]
② Iterator로 직접 돌면서 it.remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) it.remove();
}
[a, c]
Iterator 자신을 통해 지우면 "내가 지웠다"는 걸 알기 때문에 검사에 걸리지 않습니다. 지우면서 다른 처리(로그 출력, 다른 리스트에 옮기기)도 해야 할 때 씁니다.
③ 인덱스 for문을 뒤에서부터
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i).equals("b")) list.remove(i);
}
[a, c]
뒤에서부터 지우면 당겨지는 원소가 이미 지나간 쪽이라 건너뛰는 일이 없어요.
5. UnsupportedOperationException이 나면
List<String> fixed = List.of("a", "b");
fixed.removeIf(s -> s.equals("b"));
List<String> asList = Arrays.asList("a", "b", "c");
asList.remove("b");
java.lang.UnsupportedOperationException
java.lang.UnsupportedOperationException: remove
List.of()는 아예 바꿀 수 없는 리스트, Arrays.asList()는 크기를 바꿀 수 없는 리스트를 만듭니다. 지우거나 추가하려면 ArrayList로 복사하세요.
List<String> list = new ArrayList<>(List.of("a", "b"));
덤: List<Integer>에서 remove(1)의 함정
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(1);
System.out.println(nums); // [10, 30]
nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(Integer.valueOf(10));
System.out.println(nums); // [20, 30]
숫자 리스트에서 remove(1)은 "값 1"이 아니라 1번 인덱스를 지웁니다. 값으로 지우려면 Integer.valueOf()로 감싸야 해요.
정리
| 방법 | [a, b, b, c, b]에서 b 삭제 결과 |
|---|---|
| for-each + list.remove() | ConcurrentModificationException |
| 인덱스 for 앞에서부터 | [a, b, c] ← b가 남음 |
| 인덱스 for 뒤에서부터 | [a, c] |
| Iterator.remove() | [a, c] |
| removeIf | [a, c] |
참고: Oracle Java SE API 문서 — ArrayList, ConcurrentModificationException, Collection.removeIf, List.of, Arrays.asList
'프로그래밍 공부 > java' 카테고리의 다른 글
| 자바 split(".") 결과가 빈 배열일 때|점·파이프로 안 쪼개지는 이유와 빈 칸이 사라지는 문제 (split limit -1) (0) | 2026.10.09 |
|---|---|
| 자바 문자열 비교 == 와 equals() 차이|같은 "hello"인데 false가 나오는 이유 (실행 결과 포함) (0) | 2026.10.07 |
| [JAVA]기초가 든든한 데이터베이스 : 학사 정보 데이터 베이스 구축 및 활용 (0) | 2023.02.11 |
| [JAVA]기초가 든든한 데이터베이스 : 영화 정보 데이터 베이스 구축 및 활용 (0) | 2023.02.08 |
| [JAVA] 3과목의 점수를 입력받아 객체를 생성하고 성적 평균 출력하기 (0) | 2023.01.31 |
