이 예제에서 java.util.ConcurrentModificationException이 발생하지 않는 이유는 무엇입니까?
참고 : Iterator#remove()
방법을 알고 있습니다.
왜 '다음 코드 샘플에서는 이해가 안 List.remove
의 main
방법은 발생 ConcurrentModificationException
하지만, 하지 에 remove
방법.
public class RemoveListElementDemo {
private static final List<Integer> integerList;
static {
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
}
public static void remove(Integer toRemove) {
for(Integer integer : integerList) {
if(integer.equals(toRemove)) {
integerList.remove(integer);
}
}
}
public static void main(String... args) {
remove(Integer.valueOf(2));
Integer toRemove = Integer.valueOf(3);
for(Integer integer : integerList) {
if(integer.equals(toRemove)) {
integerList.remove(integer);
}
}
}
}
이유는 다음과 같습니다. Javadoc에서 말하는 것처럼 :
이 클래스의 이터레이터와 listIterator 메소드에 의해 리턴 된 이터레이터는 빠르다 : 이터레이터가 작성된 후 언제라도리스트가 구조적으로 수정되면, 이터레이터 자체의 remove 또는 add 메소드를 제외하고 어떤 식 으로든 이터레이터는 ConcurrentModificationException을 발생시킨다.
이 검사는 next()
스택 트레이스에서 볼 수 있듯이 반복자 의 메소드 에서 수행됩니다 . 그러나 우리는 true로 전달 된 next()
경우에만 메소드에 도달 할 것 hasNext()
입니다. 이는 경계가 충족되는지 확인하기 위해 각각에 의해 호출됩니다. remove 메소드에서 hasNext()
다른 요소를 리턴해야하는지 점검 할 때 두 개의 요소를 리턴했으며 이제 하나의 요소가 제거 된 후 목록에는 두 개의 요소 만 포함됩니다. 그래서 모든 것이 복숭아이며 우리는 반복으로 끝납니다. next()
호출되지 않은 메소드 에서 수행되므로 동시 수정 점검이 수행되지 않습니다 .
다음으로 두 번째 루프로 넘어갑니다. 두 번째 숫자를 제거한 후 hasNext 메소드는 더 많은 값을 반환 할 수 있는지 다시 확인합니다. 이미 두 개의 값을 반환했지만 이제는 목록에 하나만 포함됩니다. 그러나 여기 코드는 다음과 같습니다
public boolean hasNext() {
return cursor != size();
}
1! = 2, 그래서 우리는 next()
누군가가 목록을 엉망으로 만들고 예외를 발생시키는 방법 을 계속합니다 .
희망이 당신의 질문을 해결합니다.
요약
List.remove()
ConcurrentModificationException
목록에서 두 번째 마지막 요소를 제거해도 throw되지 않습니다 .
Collection
해당되는 경우 (컬렉션 자체가 아닌) 사본에서 무언가를 제거하기 위해 처리하는 한 가지 방법 . Clone
원본 컬렉션은을 통해 복사합니다 Constructor
.
이 예외는 그러한 수정이 허용되지 않을 때 객체의 동시 수정을 감지 한 메소드에 의해 발생할 수 있습니다.
특정 사례의 경우, 먼저 final
선언을지나 목록을 수정하려고 한다고 생각하지 않는 방법 이라고 생각 합니다.
private static final List<Integer> integerList;
또한 원래 목록 대신 사본을 수정하십시오.
List<Integer> copy = new ArrayList<Integer>(integerList);
for(Integer integer : integerList) {
if(integer.equals(remove)) {
copy.remove(integer);
}
}
The forward/iterator method does not work when removing items. You can remove the element without error, but you will get a runtime error when you try to access removed items. You can't use the iterator because as pushy shows it will cause a ConcurrentModificationException, so use a regular for loop instead, but step backwards through it.
List<Integer> integerList;
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
int size= integerList.size();
//Item to remove
Integer remove = Integer.valueOf(3);
A solution:
Traverse the array in reverse order if you are going to remove a list element. Simply by going backwards through the list you avoid visiting an item that has been removed, which removes the exception.
//To remove items from the list, start from the end and go backwards through the arrayList
//This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error
//for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator
for (int i=size-1; i> -1; i--) {
if (integerList.get(i).equals(remove) ) {
integerList.remove(i);
}
}
This snippet will always throw a ConcurrentModificationException.
The rule is "You may not modify (add or remove elements from the list) while iterating over it using an Iterator (which happens when you use a for-each loop)".
JavaDocs:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException.
Hence if you want to modify the list (or any collection in general), use iterator, because then it is aware of the modifications and hence those will be handled properly.
Hope this helps.
I had that same problem but in case that I was adding en element into iterated list. I made it this way
public static void remove(Integer remove) {
for(int i=0; i<integerList.size(); i++) {
//here is maybe fine to deal with integerList.get(i)==null
if(integerList.get(i).equals(remove)) {
integerList.remove(i);
}
}
}
Now everything goes fine because you don't create any iterator over your list, you iterate over it "manually". And condition i < integerList.size()
will never fool you because when you remove/add something into List size of the List decrement/increment..
Hope it helps, for me that was solution.
This runs fine on Java 1.6
~ % javac RemoveListElementDemo.java
~ % java RemoveListElementDemo
~ % cat RemoveListElementDemo.java
import java.util.*;
public class RemoveListElementDemo {
private static final List<Integer> integerList;
static {
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
}
public static void remove(Integer remove) {
for(Integer integer : integerList) {
if(integer.equals(remove)) {
integerList.remove(integer);
}
}
}
public static void main(String... args) {
remove(Integer.valueOf(2));
Integer remove = Integer.valueOf(3);
for(Integer integer : integerList) {
if(integer.equals(remove)) {
integerList.remove(integer);
}
}
}
}
~ %
If you use copy-on-write collections it will work; however when you use list.iterator(), the returned Iterator will always reference the collection of elements as it was when ( as below ) list.iterator() was called, even if another thread modifies the collection. Any mutating methods called on a copy-on-write–based Iterator or ListIterator (such as add, set, or remove) will throw an UnsupportedOperationException.
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class RemoveListElementDemo {
private static final List<Integer> integerList;
static {
integerList = new CopyOnWriteArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
}
public static void remove(Integer remove) {
for(Integer integer : integerList) {
if(integer.equals(remove)) {
integerList.remove(integer);
}
}
}
public static void main(String... args) {
remove(Integer.valueOf(2));
Integer remove = Integer.valueOf(3);
for(Integer integer : integerList) {
if(integer.equals(remove)) {
integerList.remove(integer);
}
}
}
}
Change Iterator for each
into for loop
to solve.
And the Reason is:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException.
--Referred Java Docs.
In my case I did it like this:
int cursor = 0;
do {
if (integer.equals(remove))
integerList.remove(cursor);
else cursor++;
} while (cursor != integerList.size());
Check your code man....
In the main method you are trying to remove the 4th element which is not there and hence the error. In the remove() method you are trying to remove the 3rd element which is there and hence no error.
'Programing' 카테고리의 다른 글
“git branch”와“git checkout -b”의 차이점은 무엇입니까? (0) | 2020.05.23 |
---|---|
PHP 5는 엄격한 표준 오류를 비활성화 (0) | 2020.05.23 |
const로 변수를 초기화하려고 할 때 오류 "초기화 요소가 일정하지 않습니다" (0) | 2020.05.23 |
어셈블리 이름 얻기 (0) | 2020.05.23 |
bash heredoc에서 변수 사용 (0) | 2020.05.23 |