问题
I know if would be trying to remove from collection looping through it with the simple loop I will be getting this exception: java.util.ConcurrentModificationException
. But I am using Iterator and it still generates me this exception. Any idea why and how to solve it?
HashSet<TableRecord> tableRecords = new HashSet<>();
...
for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) {
TableRecord record = iterator.next();
if (record.getDependency() == null) {
for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) {
TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception
if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) {
tableRecords.remove(record);
}
}
}
}
回答1:
You must use iterator.remove()
instead of tableRecords.remove()
You can remove items on a list on which you iterate only if you use the remove method from the iterator.
EDIT :
When you create an iterator, it starts to count the modifications that were applied on the collection. If the iterator detects that some modifications were made without using its method (or using another iterator on the same collection), it cannot guarantee anymore that it will not pass twice on the same element or skip one, so it throws this exception
It means that you need to change your code so that you only remove items via iterator.remove (and with only one iterator)
OR
make a list of items to remove then remove them after you finished iterating.
回答2:
Iterator fail-fast property checks for any modification in the structure of the underlying collection everytime we try to get the next element. If there are any modifications found, it throws ConcurrentModificationException. All the implementations of Iterator in Collection classes are fail-fast by design except the concurrent collection classes like ConcurrentHashMap and CopyOnWriteArrayList.
Source: Google
You'll better understand with an example written below:-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class IteratorExp {
public static void main(String... q) {
//CASE - ONE
List<String> strList = new ArrayList<>(Arrays.asList("a", "b", "c"));
Iterator<String> itr = strList.iterator();
/*
* strList.add("e"); strList.add("f"); strList.add("g");
*/
while (itr.hasNext()) {
System.out.println(itr.next());
}
/*
* Exception in thread "main" java.util.ConcurrentModificationException
* at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at
* java.util.ArrayList$Itr.next(Unknown Source) at
* IteratorExp.main(IteratorExp.java:14)
*/
//CASE - TWO
List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0));
Iterator<Integer> itrOne = intList.iterator();
Iterator<Integer> itrTwo = intList.iterator();
for (; itrOne.hasNext();) {
if (itrOne.next().equals(5)) {
itrOne.remove(); // #1
//intList.remove(itrOne.next()); // #2
}
}
for (; itrTwo.hasNext();) {
if (itrTwo.next().equals(5)) {
itrTwo.remove(); // #1
//intList.remove(itrTwo.next()); // #2
}
}
/*
* Exception in thread "main" java.util.ConcurrentModificationException
* at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at
* java.util.ArrayList$Itr.next(Unknown Source) at
* IteratorExp.main(IteratorExp.java:35)
*/
}
}
回答3:
The contract for HashSet's iterator is that you can't remove from the hashset other than through that spcific iterator's remove method. From the perspective of dependencyIt
, you have removed an item other than by calling its remove
method so it throws a ConcurrentModificationException
.
It seems you want to remove records from your hashset when they have the same record id. Wouldn't it be easier to override your records's equals
and hashcode
methods to ensure that records with the same id equal and have the same hashcode? (if that makes sense of course)
回答4:
The issue is that you have two iterators in scope at the same time and they're "fighting" with each other. The easiest way to fix the issue is just to bail out of the inner loop if you find a match:
for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) {
TableRecord record = iterator.next();
if (record.getDependency() == null) {
for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) {
TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception
if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) {
iterator.remove();
break; // ADD THIS LINE
}
}
}
}
Java Iterator
s are intended to "fail fast" whenever their underlying container is changed without having been changed using the Iterator
. You're using nested iterators, so any remove()
operation issued to one will cause the other to throw an Exception
if it continues to be used. For this reason, if you need to issue a remove()
, then you'll need to do it on the "outer" iterator (which you're doing) and discontinue use of the second iterator afterwards (which the added break
statement does).
来源:https://stackoverflow.com/questions/16965484/java-util-concurrentmodificationexception-with-iterator