How to fix an exception in the stream "main" java.util.ConcurrentModificationException
I have 2 HashMap<Integer,Point3D> object names positiveCoOrdinate and negativeCoOrdinates .
I check PositiveCoOrdinates following condition. If it satisfies that the corresponding point is added to negativeCoOrdinates and removed from PositiveCoOrdinates .
HashMap<Integer, Point3d> positiveCoOrdinates=duelList.get(1); HashMap<Integer, Point3d> negativecoOrdinates=duelList.get(2); //condition Set<Integer> set=positiveCoOrdinates.keySet(); for (Integer pointIndex : set) { Point3d coOrdinate=positiveCoOrdinates.get(pointIndex); if (coOrdinate.x>xMaxValue || coOrdinate.y>yMaxValue || coOrdinate.z>zMaxValue) { negativecoOrdinates.put(pointIndex, coOrdinate); positiveCoOrdinates.remove(pointIndex); } } When adding, deleting time, I get the following error.
Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(Unknown Source) at java.util.HashMap$KeyIterator.next(Unknown Source) at PlaneCoOrdinates.CoordinatesFiltering.Integration(CoordinatesFiltering.java:167) at PlaneCoOrdinates.CoordinatesFiltering.main(CoordinatesFiltering.java:179) For my testing, I mention the statement System.out.println(coOrdinate.x); inside If condition.it working fine.
If I add 2 lines (as I mention above) inside the If condition, it throws an error.
How can I fix this.
Thanks.
The easiest way is to make a copy of keySet:
Set<Integer> set= new HashSet<Integer>(positiveCoOrdinates.keySet()); The problem is due to the modification of positiveCoOrdinates when you use an Iterator that iterates with the keys.
You can also reorganize your code and use an iterator over a set of records. This will be the best approach.
Set<Entry<Integer, Point3d>> entrySet = positiveCoOrdinates.entrySet(); for (Iterator<Entry<Integer, Point3d>> iterator = entrySet.iterator(); iterator.hasNext();) { Entry<Integer, Point3d> entry = iterator.next(); Point3d coOrdinate = entry.getValue(); if (coOrdinate.x > xMaxValue || coOrdinate.y > yMaxValue || coOrdinate.z > zMaxValue) { Integer pointIndex = entry.getKey(); negativecoOrdinates.put(pointIndex, coOrdinate); iterator.remove(); } } You cannot remove() from an iterated collection when using the extended for-each loop. The for-each loop uses an Iterator<Integer> implicitly. JavaDoc clearly states that
The iterators returned by the collection methods "class view" class are unsuccessful: if the map is structurally modified at any time after creating the iterator, in any way, except for the iterator's own
remove()method, the iterator will throw aConcurrentModificationException. Thus, in the face of parallel modifications , the iterator crashes quickly and cleanly, and not at the risk of arbitrary, non-deterministic behavior at an undefined time in the future.
The for-each loop creates an iterator inside and uses it to move around the set. Then you change the structure of the set ... and the iterator should fail. The point is that you do not have access to iterator methods, so you must explicitly use Iterator<Integer> . The generated byte bytecode will be the same, with the only difference being that you can remove items from the list when you move it.
Set<Integer> set = positiveCoOrdinates.keySet(); for (Iterator<Integer> iterator = set.iterator(); iterator.hasNext(); ) { Integer pointIndex = iterator.next(); Point3d coOrdinate = positiveCoOrdinates.get(pointIndex); if (coOrdinate.x>xMaxValue || coOrdinate.y>yMaxValue || coOrdinate.z>zMaxValue) { negativecoOrdinates.put(pointIndex, coOrdinate); iterator.remove(pointIndex); // this line changed! } } If you are not familiar with iterators and their functions, see the Oracle collections tutorial :
An
Iteratoris an object that allows you to go through collections and selectively remove items from the collection, if desired. You get anIteratorfor the collection by calling itsiterator()method.Note that
Iterator.remove()is the only safe way to change the collection during iteration; behavior is unspecified if the base collection is modified in any other way while the iteration is Running.Use an
Iteratorinstead of afor-eachconstruct when you need to:
- Delete current item. The
for-eachconstruct hides the iterator, so you cannot callremove(). Therefore, thefor-eachconstruct is not used for filtering.
If you want to change the collection at run time, you need to use Iterator instead of extended for loop. Because advanced for loop provides read-only functions. The following is an example of an Iterator:
Iterator<Entity> iterator = collection.Iterator(); while(iterator.hasNext()){ //DO Your Stuff iterator.remove(); // this function call remove the element from collection at run time } As Rene noted, the cause of this very common problem is the simultaneous modification of the collection when it is being read by another.
You can use ConcurrentHashMap or collections such as CopyOnWriteArrayLit , but be careful that these approaches can be a bit expensive and simple code changers to prevent reading of the same collection, while iterating this will solve the type of problems.