Java: Can not change list of iterators - java

I have a list of ListIterator<PointF> as a class field. I fill it in method grow(). When i try to use iterators from this list i get ConcurrentModificationException.
ListIterator<ListIterator<PointF>> i = mPoints.listIterator();
while (i.hasNext()) {
ListIterator<PointF> j = i.next();
if (j.hasNext())
PointF tmp = j.next(); // Exception here
}
I have no idea why does this code causes exeption in any method besides grow()

If the underlying list changes, the iterator that was obtained before that throws ConcurrentModificationException. So don't store iterators in instance fields.

What we can say for sure is that a ConcurrentModificationException means that the underlying iterable has been modified at some point after your call to get the iterator.
This does not always mean concurrent as in multi-threaded; one can easily trigger this exception by iterating through a list and deleting elements during the loop. So, if there are no other threads potentially modifying this, then we can say that the current thread has modified an iterator's underlying data structure at some point.
There's not enough code here to be sure, but your practice of storing iterators is a little suspicious. When did you add the (inner) iterators to mPoints? If the collection they refer to changes at any time after the iterator was created, it will throw this exception when invoked. Hence as soon as you add an iterator to the mPoints collection, the iterator's data structure is effectively locked for changes, and yet this won't be very clear in the code at all.
So I suspect this is the root cause of your problem. Unless it's for a very short term (and usually within a single lexical scope, e.g. a single method invocation) it's probably a bad idea to store iterators for the reason you're seeing. It might be better to store a reference to the underlying collections themselves, and then create the iterators during the code block above, something like:
ListIterator<Iterable<PointF>> i = mPoints.listIterator();
while (i.hasNext()) {
Iterator<PointF> j = i.next().iterator();
if (j.hasNext())
PointF tmp = j.next();
}
Then again the exact solution depends on the general architecture of your method. The main thing to bear in mind is don't store iterators long-term, because it's almost impossible to make this work reliably. Even if it does work right now, it creates a kind of invisible dependency between different parts of your code that will almost invariably be broken by someone implementing what should be a trivial change.

Related

List throwing ConcurrentModificationException passed to CompletableFutures

Scenario 1:::
So I have list of params, which is passed to 2 methods which calls web service and gets the data. These to methods just do stream.filter.collect on the list of params to get the needed parameter for rest call.
Now I have made the 2 calls parallel using CompletableFutures.
Can this throw ConcurrentModifcation exception?
Scenario 2:::
Similar setup as above , just that now one method changes the list of params and adds some objects to it. I know this is throwing Concurrent Modification exp. Should I just make list as copyonWriteArraylist or create new list with deep copy to avoid any further problems.
Scenario #1: Probably not, but your description is too vague to be sure.
Scenario #2: Most absolutely.
The only thing you need for CoModEx to occur is that the list is changed in any way. Be it add, addAll, clear, remove, retainAll, or any other method on List that has the effect of changing the list itself. Even fetching a sublist and changing THAT (as changes to sublist are visible from the 'outer' list that the sublist was created from).
CoModEx, despite the use of the word 'concurrent', has zip squat to do with threads. In fact, messing with a list from two threads simultaneously is one of the few ways you can break things (methods no longer do what their javadoc says they should) without causing a ConcurrentModificationException (will depend on how the race condition goes).
Here is a trivial way to get a CoModEx:
var list = new ArrayList<String>();
list.add("Hello");
list.add("World");
for (String item : list) if (item.equals("Hello")) list.remove(item);
That will throw it. Every time. CoModEx is thrown by iterators (and the for (x:y) constructor will implicitly create iterators, as does x.stream()..., which creates a spliterator, which also does this) when the underlying data structure was changed in any way that is not directly done by the (spl)iterator itself. For example, this is the one way you get to remove things from your own list using an iterator that does not result in CoModEx:
var it = list.iterator();
while (it.hasNext()) {
if (it.next().startsWith("Hello")) it.remove();
}
Note I'm calling iterator's remove, not list's remove, which would have caused CoModEx: That would change the underlying list (and not via the iterator directly), therefore any operation on an iterator created before the modification will throw CoModEx.
So, this is the flow:
You create an iterator from list, by entering for (String item : list).
That iterator's hasNext() is invoked to check if the for loop should be entered. It returns true
That iterator's next() is invoked for the first loop; Hello is returned.
Due to the code inside the for loop, list.remove("Hello") is invoked. This 'invalidates' all iterators that were created by this list so far.
the for loop loops, and invokes hasNext() to check if it should loop again.
hasNext realizes that it is invalid, and throws CoModEx.
ArrayList does this by having a counter which is incremented every time anything changes, and all iterators remember the value of the counter when created, and check that the list's counter value is equal to their own. If not, they throw CoModEx. Other list impls can use different mechanisms if they desire. Some go out of their way to actually allow this (such as CopyOnWriteArrayList, which explicitly details how it DOES let you modify itself during iteration).
If multiple threads are involved, all bets are off - those counter writes are not synchronized and therefore may or may not be visible by the threads involved. Don't access the same list from different threads unless you really know what you are doing.

Can I use many listIterators sequentially to mutate or remove list elements from an ArrayList in Java?

I am relying on list iterators to move through a list of characters. This is a single-threaded program and I use listIterator objects sequentially in 4 different methods. Each method has the same setup:
private void myMethod(ArrayList<Integer> input) {
ListIterator<Integer> i = input.listIterator();
while (i.hasNext()) {
Integer in = i.next();
if (in < 10)
i.remove();
else
i.set(in*in); // because its lucky
}
}
With this pattern, on the second iterator the following Exception is thrown:
java.util.ConcurrentModificationException
However, looking at the javadocs I don't see this Exception in the Exceptions thrown nor do I see a method to close the iterator after I am done. Am I using the listIterator incorrectly? I have to iterate over the same ArrayList multiple times, each time conditionally removing or mutating each element. Maybe there is a better way to iterate over the ArrayList and this use-case is not best solved by a ListIterator.
java docs for ListIterator
This is explained in the ArrayList javadoc, you are modifying the list with remove() and set() while using an Iterator:
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. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
It’s hard to give diagnostic for a problem when the shown code clearly isn’t the code that produced the exception, as it doesn’t even compile. The remove method of Iterator doesn’t take arguments and the set method is defined on ListIterator, but your code declares the variable i only as Iterator.
A fixed version
private void myMethod(ArrayList<Integer> input) {
ListIterator<Integer> i = input.listIterator();
while (i.hasNext()) {
Integer in = i.next();
if (in < 10)
i.remove();
else
i.set(in*in);
}
}
would run without problems. The answer to your general question is that each modification invalidates all existing iterators, except the one used to make the modification when you did use an iterator for the modification and not the collection interface directly.
But in your code, there is only one iterator, which is only created and used for this one operation. As long as there is no overlapping use of iterators to the same collection, there is no problem with the invalidation. Iterators existing from previous operations are abandoned anyway and the iterators used in subsequent operations do not exist yet.
Still, it’s easier to use
private void myMethod(ArrayList<Integer> input) {
input.removeIf(in -> in < 10);
input.replaceAll(in -> in*in);
}
instead. Unlike the original code, this does two iterations, but as explained in this answer, removeIf will be actually faster than iterator based removal in those cases, where performance really matters.
But still, the problem persists. The shown code can’t cause a ConcurrentModificationException, so your actual problem is somewhere else and may still be present, regardless of how this one method has been implemented.
I am not knowledgable enough about Java ListIterators to answer the question but it appears I have run into the XY problem here. The problem seems to be better solved with Java Streams to remove the element or map the element into a new ArrayList by exercising a function on each element in the original ArrayList.
private ArrayList<Integer> myMethod(ArrayList<Integer> input) {
ArrayList<Integer> results = input.stream().filter(
in -> (in < 10)).collect(Collectors.toCollection(ArrayList::new));
results = input.stream().map(
in -> in*in).collect(Collectors.toCollection(ArrayList::new));
return results;
}

Is there a way to opt for "unspecified behavior" rather than ConcurrentModificationException?

I know that code like
for ( Object o: collection){
if (condition(i)){
collection.remove(i);
}
}
will throw a ConcurrentModificationException, and I understand why: modifying the collection directly could interfere with the Iterator's ability to keep track of its place, by, for instance, leaving it with a reference to an element that's no longer a part of the collection, or causing it to skip over one that's just been added. For code like the above, that's a reasonable concern, however, I would like to write something like
for (Object o: set){// set is an instance of java.util.LinkedHashSet
if (condition(o)){
set.remove(other(o));
}
}
Where other(o) is guaranteed to be "far" from o in the ordering of set. In my particular implementation it will never be less than 47 "steps" away from o. Additionally, if if condition(o) is true, the loop in question will be guaranteed to short-circuit well before it reaches the place where other(o) was. Thus the entire portion of the set accessed by the iterator is thoroughly decoupled from the portion that is modified. Furthermore, the particular strengths of LinkedHashSet (fast random-access insertion and removal, guaranteed iteration order) seem particularly well-suited to this exact sort of operation.
I suppose my question is twofold: First of all, is such an operation still dangerous given the above constraints? The only way that I can think that it might be is that the Iterator values are preloaded far in advance and cached, which I suppose would improve performance in many applications, but seems like it would also reduce it in many others, and therefore be a strange choice for a general-purpose class from java.util. But perhaps I'm wrong about that. When it comes to things like caching, my intuition about efficiency is often suspect. Secondly, assuming this sort of thing is, at least in theory, safe, is there a way, short of completely re-implementing LinkedHashSet, or sacrificing efficiency, to achieve this operation? Can I tell Collections to ignore the fact that I'm modifying a different part of the Set, and just go about its business as usual? My current work-around is to add elements to an intermediate collection first, then add them to the main set once the loop is complete, but this is inefficient, since it has to add the values twice.
The ConcurrentModificationException is thrown because your collection may not be able to handle the removal (or addition) at all times. For example, what if the removal you performed meant that your LinkedHashSet had to reduce/increase the space the underlying HashMap takes under the hood? It would have to make a lot of changes, which would possibly render the iterator useless.
You have two options:
Use Iterator to iterate elements and remove them, e.g. calling Iterator iter = linkedHashSet.iterator() to get the iterator and then remove elements by iter.remove()
Use one of the concurrent collections available under the java.util.concurrent package, which are designed to allow concurrent modifications
This question contains nice details on using Iterator
UPDATE after comments:
You can use the following pattern in order to remove the elements you wish without causing a ConcurrentModificationException: gather the elements you wish to remove in a List while looping through the LinkedHashSet elements. Afterwards, loop through each toBeDeleted element in the list and remove it from the LinkedHashSet.

Concurrent Modification Exception with ArrayList

I have a problem with ConcurrentModificationException.
I have an ArrayList of Complex class that I defined. I added two Complexes, and try to do a for each loop but I get the ConcurrentModificationException. However, when I remove that line, I get no error. I need those initial points (1,0), (-1,0) to calculate points that I will need later.
for (Iterator<Complex> num = dots.iterator(); num.hasNext();) {
// ConcurrentModificationException
Complex aComplex = num.next();
// clone it and clear
temp.add(new Complex(aComplex));
dots.clear();
}
You cannot modify a collection while iterating on it. If you would move dots.clear(); and temp.clear() outside iterations; it will get resolved. If needed you can create a flag whenever these collections need to be cleared; and after iteration is over you can clear them.
Most iterators implementations don't allow the underlying structure to be modified unless it's with the defined semantics on the iterator itself (the remove method).
So, in all the sections of the code where you are clearing the structure while iterating over it, you will get a ConcurrentModificationException.

ConcurrentModificationException and HashSet.iterator()

I have a for loop like
for (int neighbour : neighbours) {
Where I may modify neighbours within the loop. Found that thats the cause of ConcurrentModificationException. And read from https://stackoverflow.com/a/8189527/292291
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.
So I tried:
neighboursItr = neighbours.iterator();
while (neighboursItr.hasNext()) {
// try disconnecting vertices
neighbour = neighboursItr.next();
But that doesnt fix the problem. Why?
Are you calling neightbours.remove(neighbour)? In that case, that is the problem. You need to call neightboursItr.remove() instead.
Have you considered creating a new HashSet with desired state? I mean you can iterate through the neighbours and add to the newNeighbours whatever you want.
You may only modify the collection using methods of the iterator while iterating on the collection. So you may call neighboursItr.remove(), but you may not add an element to the collection using neighbours.add(), for example.
You cannot modify collection while iterating. The only exception is using iterator.remove() method (if it is supported by target collection).
The reason is that this is how iterator works. It has to know how to jump to the next element of the collection. If collection is being changed after iterator creation it cannot do this and throws exception.
There are several solutions for this problem. For example if you want to add elements to existing collection during iteration you can create yet another collection where you store new elements and then add all these elements after your iteration is finished.

Categories