우리는 당신이 다음을 할 수 없다는 것을 알고 있습니다 ConcurrentModificationException
.
for (Object i : l) {
if (condition(i)) {
l.remove(i);
}
}
그러나 이것은 때때로 작동하지만 항상 그런 것은 아닙니다. 특정 코드는 다음과 같습니다.
public static void main(String[] args) {
Collection<Integer> l = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
l.add(4);
l.add(5);
l.add(6);
}
for (int i : l) {
if (i == 5) {
l.remove(i);
}
}
System.out.println(l);
}
이것은 물론 다음과 같은 결과를 낳습니다.
Exception in thread "main" java.util.ConcurrentModificationException
여러 스레드가 수행하지 않더라도. 어쨌든.
이 문제에 대한 가장 좋은 해결책은 무엇입니까? 이 예외를 발생시키지 않고 루프에서 컬렉션에서 항목을 제거하려면 어떻게해야합니까?
나는 또한 Collection
여기에 반드시 필요한 것은 아니지만 임의의 것을 사용 ArrayList
하므로 의지 할 수 없습니다 get
.
답변
Iterator.remove()
안전하므로 다음과 같이 사용할 수 있습니다.
List<String> list = new ArrayList<>();
// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
// Iterator<String> iterator = list.iterator();
// while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String string = iterator.next();
if (string.isEmpty()) {
// Remove the current element from the iterator and the list.
iterator.remove();
}
}
참고 Iterator.remove()
반복하는 동안 컬렉션을 수정할 수있는 유일한 안전한 방법입니다; 반복이 진행되는 동안 기본 컬렉션이 다른 방식으로 수정되면 동작이 지정되지 않습니다 .
마찬가지로, 항목이 있고 항목 ListIterator
을 추가 하려는 경우을 사용할 수 ListIterator#add
있는 것과 같은 이유로 사용할 Iterator#remove
수 있습니다.
귀하의 경우 목록에서 제거하려고 시도했지만 내용을 반복 put
하는 Map
동안 동일한 제한이 적용됩니다 .
답변
이것은 작동합니다 :
Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
if (iter.next() == 5) {
iter.remove();
}
}
foreach 루프는 반복을위한 구문 설탕이기 때문에 반복자를 사용하면 도움이되지 않지만 …이 .remove()
기능을 제공한다고 가정했습니다 .
답변
Java 8 에서는 새로운 removeIf
방법을 사용할 수 있습니다 . 귀하의 예에 적용 :
Collection<Integer> coll = new ArrayList<>();
//populate
coll.removeIf(i -> i == 5);
답변
질문에 이미 답변되었으므로 가장 좋은 방법은 반복자 객체의 remove 메소드를 사용하는 것이므로 오류 "java.util.ConcurrentModificationException"
가 발생 하는 장소의 세부 사항으로 이동합니다 .
모든 컬렉션 클래스는 반복자 인터페이스를 구현하고 같은 방법을 제공하는 개인 클래스가 next()
, remove()
하고 hasNext()
.
다음 코드는 다음과 같습니다.
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch(IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
여기서 방법 checkForComodification
은 다음과 같이 구현됩니다.
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
보시다시피 컬렉션에서 요소를 명시 적으로 제거하려고하면 와 modCount
다르게되어 expectedModCount
예외가 발생합니다.ConcurrentModificationException
합니다.
답변
언급 한대로 직접 반복자를 사용하거나 두 번째 콜렉션을 유지하고 제거하려는 각 항목을 새 콜렉션에 추가 한 다음 끝에 AllAll을 제거 할 수 있습니다. 이를 통해 메모리 사용 및 CPU 시간이 증가하면서 for-each 루프의 유형 안전성을 계속 유지할 수 있습니다 (실제로 큰 목록이나 오래된 컴퓨터가 없다면 큰 문제는 아닙니다)
public static void main(String[] args)
{
Collection<Integer> l = new ArrayList<Integer>();
Collection<Integer> itemsToRemove = new ArrayList<>();
for (int i=0; i < 10; i++) {
l.add(Integer.of(4));
l.add(Integer.of(5));
l.add(Integer.of(6));
}
for (Integer i : l)
{
if (i.intValue() == 5) {
itemsToRemove.add(i);
}
}
l.removeAll(itemsToRemove);
System.out.println(l);
}
답변
이러한 경우에 일반적인 트릭은 (뒤로?) 거꾸로가는 것입니다.
for(int i = l.size() - 1; i >= 0; i --) {
if (l.get(i) == 5) {
l.remove(i);
}
}
즉, Java 8에서 예를 들어 removeIf
또는 filter
스트림 에서 더 나은 방법을 사용하게되어 기쁩니다 .
답변
for 루프가있는 Claudius 와 동일한 답변 :
for (Iterator<Object> it = objects.iterator(); it.hasNext();) {
Object object = it.next();
if (test) {
it.remove();
}
}