Concurrent modification when next() has exhausted elements - java

Been reading up on Iterators recently. Suppose that we want to build a fail-fast Iterator for a given container class. At some point in time, the following code has executed;
Iterator<Object> it = myContainer.iterator();
while(it.hasNext())
System.out.println(it.next());
Then, we perform a modification on myContainer, using, say, a method called remove(int index):
myContainer.remove(2); // Assume at least 3 elements contained
If it.next() is called now, and based on the official docs, should we throw an instance of ConcurrentModificationException, or NoSuchElementException? Perhaps more importantly, is this in any way inferrable from the official docs?

You should throw a ConcurrentModificationException. A NoSuchElementException indicates there are no more elements in the iterator. When you encounter a concurrent modification, it always overrules the NoSuchElementException. At least that how the Arraylistand LinkedList are doing it.

Related

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;
}

When will be concurrent modification exception will be thrown and how iterator remove method will work?

According to the javadocs,conncurrent modification exception will be thrown when we will try to structurally modify the collection while iterating over it.Only iterator remove method will not throw concurrent modification exception.
I have analyzed following cases where concurrent modification exception will not be thrown:
List<Integer> var=new ArrayList<Integer>();
var.add(3);
var.add(2);
var.add(5);
1st case:
for(int i = 0; i<var.size(); i++){
System.out.println(var.get(i));
var.remove(i);
}
2nd Case:
for(Integer i:var){
System.out.println(i);
if(i==2){
var.remove("5");
}
}
1)Can anyone please explain how these cases are not throwing Conncurrent Modification Exception?
2)Can anyone please tell how iterator.remove internally works?
The first implementation isn't using an iterator, so of course it doesn't throw. It's only Iterators that throw (though even the indexed for-loop will be corrupted, e.g. if you remove an element and then the indexes of all the other elements shift out from under your iteration). Java just won't be able to tell you.
The second implementation isn't changing the contents. var.remove("5") attempts to remove the string "5", which isn't present (the list only contains integers), so no change happens. Additionally, i == 2 is dangerous when i is a boxed capital-I Integer; consider doing for (int i : var) instead.
Finally, no implementation guarantees that a ConcurrentModificationException will be thrown; it only tries to do so to warn you your iterator has been corrupted. Whether or not an exception is thrown, the iterator is corrupted by modifications performed while an iteration is in progress, Java's just trying to warn you.
According to the Javadoc of ArrayList:
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.
So, the conditions under which a CME will be thrown are:
There must be an iterator (or a list iterator)
You must perform a structural modification of the list not via the iterator (i.e. add or remove an element)
And, perhaps not quite so obviously:
After the structural modification, you must call a method on an iterator created before the structural modification.
i.e. the iterator doesn't attempt to detect the CME until you call next() or remove() on it.

Deque remove is not throwing ConcurrentModificationException

The Deque class' Javadoc says:
The iterators returned by this class's iterator method are fail-fast:
If the deque is modified at any time after the iterator is created, in
any way except through the iterator's own remove method, the iterator
will generally 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.
However, the below program behaves differently:
[EDIT]: I am getting error while pasting the whole code "An error occurred submitting the edit". Whew! C'mon SO.
// create an empty array deque with an initial capacity
Deque deque = new ArrayDeque(8);
// use add() method to add elements in the deque
deque.add(15);
deque.add(22);
deque.add(25);
deque.add(20);
System.out.println("printing elements using iterator:");
for(Iterator itr = deque.iterator();itr.hasNext();) {
System.out.println(itr.next());
deque.remove(); //deque is modifed after the iterator is created
}
I expected it to throw ConcurrentModificationException, but it simply prints the following output:
printing elements using iterator:
15
22
25
20
Any idea why?
It looks like it's because you consume the first element of the iterator before removing it. If you change your code to
for(Iterator itr = deque.iterator();itr.hasNext();) {
deque.remove();
System.out.println(itr.next());
}
then you'll see the exception. Your original implementation does indeed contradict the documentation.
However, looking in the implementation of ArrayDeque's iterator implementation, the next() method has this code:
E result = (E) elements[cursor];
// This check doesn't catch all possible comodifications,
// but does catch the ones that corrupt traversal
if (tail != fence || result == null)
throw new ConcurrentModificationException();
Note the following paragraph in the Deque's Javadoc:
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

ConcurrentModificationException thrown on iterator.remove

I'm getting a ConcurrentModificationException despite using the iterator to perform the remove operation. Any ideas as to why?
for (Iterator<Thread> iter = threads.iterator(); iter.hasNext();) {
Thread hook = iter.next();
if(someCondition){
iter.remove();
}
}
From JavaDoc Iterator.remove():
Removes from the underlying collection the last element returned by
the iterator (optional operation). This method can be called only once
per call to next. The behavior of an iterator is unspecified if the
underlying collection is modified while the iteration is in progress
in any way other than by calling this method.
It seem that the behavior is depended to the collection. Also as aioobe pointed out. I can reproduce ConcurrentModificationException when I modify the collection somewhere else. Using only Iterator interface, I can only reproduce IllegalStateException.
Because you have a modification concurrent to using an Iterator, which is not supported. Either iterate a list clone, or use a CopyOnWriteArrayList.
Or memory what to remove in a new list, and call list.removeAll(whatIWantedToRemove) afterwards.

Concurrent Modification Exception thrown by .next from Iterator

Not sure exactly what's wrong here:
while(itr.hasNext())
{
Stock temp =itr.next();
}
This code is throwing a ConcurrentModificationException at itr.next();
Initialization for the iterator is
private Iterator<Stock> itr=stockList.iterator();
Any ideas?
[The basic code was copied directly from professor's slides]
This could be happening because of two reasons.
Another thread is updating stockList either directly or through its iterator
In the same thread, maybe inside this loop itself, the stockList is modified (see below for an example)
The below codes could cause ConcurrentModificationException
Iterator<Stock> itr = stockList.iterator();
while(itr.hasNext())
{
Stock temp = itr.next();
stockList.add(new Stock()); // Causes ConcurrentModificationException
stockList.remove(0) //Causes ConcurrentModificationException
}
Some other thread is modifying the underlying collection? I suspect that there is code above what you are showing us that causes the problem: a mod to the collection between the call to iterator() and the loop.
Most plausible reason is that some code has modified the underlying collection after you obtained your iterator.
Form javadoc:
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.

Categories