ConcurrentModificationException and HashSet.iterator() - java

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.

Related

How to get forward iterator that starts from the last element

I have a LinkedList. Suppose that I'm inserting an element at the end and I want to save the position where was it inserted, so that I can call a function on an element next to it, whatever manages to get into this collection later. Is it possible with Java iterators? Many thanks.
Just to recollect, I'm not interested in reverse iteration. The application will be multithreaded, hence the weird requirement.
You can call List#listIterator(int index) with index = size() -1 to get an iterator to the current last element of the list. See documentation: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html
However, you are going to be stuck from there.
Whether the List implementation you are using isn't thread-safe, which is the case for LinkedList, ArrayList and most others, and any attempt to use the iterator after the list has been structurally modified is going to result in a ConcurrentModificationException being thrown.
A list is structurally modified when its size changes, i.e. on additions and removals.
Or the List implementation you are using is thread-safe, in which case you have no guaranty that the iterator will have access to the elements added to the list after the creation of the iterator.
For example, it wouldn't be the case with CopyOnWriteArrayList, for which the iterator iterates through data as it was at creation (like a snapshot).
You must find an implementation of List that clearly describe this behavior and explicitly say it in its documentation. As far as I know, there doesn't exist any that allow it, at least in the standard library.

Difference Between Iterator remove vs ArrayList remove?

To remove element from ArrayList, we can use-
Iterator remove() is used while iteration.
For ArrrayList remove() no iteration required.
Syntax is different in those cases. So
Do both use same logic internally?
Is there any more difference than logic?
Which one is better?
Any detailed explanation/link is highly appreciated.
An iterator might throw ConcurrentModificationException if an element is removed from the underlying collection in another way than the iterator's own remove() method.
So if you need to remove elements while iterating over a collection, you're allowed to do that with Iterator.remove() but you can't do that with Collection.remove() without risking to get an exception.
remove is a method that should be implemented (if no, it should throw UnsupportedOperationException) by all objects that are Iterable (implement interface Itarable). The way it works depends always on the object that implements it.
That means an ArrayList can implement it in a totally different way then i.e. LinkedList.
Removing object in Iterator requires You to iterate (find) the object You want to remove.
Using a remove method in ArrayList (there is no delete I can see in Javadoc: http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html) finds the object for You and deletes it. It actually shifts objects in an underlying arrays to fill the "gap" You created by removing the object, so if You want to remove items in a list often, You could use LinkedList Instead.
Additionally while You are iterating through a list, You will cause an exception if You want to modify the collection in some other way than via iterator methods.
The exact answers to Your questions are:
1.No they use diferent logic, and additionally Iterator might even not allow to delete object (UnsupportedOperationException)
2.You cannot remove object by ArrayList remove while You are itereating, and to remove object at position 4 in ArrayList by using Iterator You would have to iterate 4 times "manually".
3.It depends whether You allready know what object do You want to remove, or first You check all the objects and decide whether to delete, during the iteration process. Additionally - If You want to delete objects often, You better use LinkedList, instead of ArrayList.

what will happen if modify a collection while iterating without throwing the ConcurrentModificationException

As all known We can't modify a non-thread-safe collection while iterating it since it will throw a ConcurrentModificationException
But what I want to know is that what will happen if it would not throw a Exception and let the iteration and modification happened concurrently.
For example, remove an element from a HashMap while iterating it.
Remove. Since remove operation would not change the length of the underlying table in the HashMap, I think that's not an issue to the iteration.
Put. Maybe problem only occurs when the Put triggers resize() since the underlying table will be shuffled.
Is my analysis correct?
Short answer: no, your analysis is not correct.
If you remove something from a collection while you're iterating over it (without using the iterator), the iterator doesn't have a good way to keep track of where it is. Use a simpler example: List. Say the iterator is at index 10, and you remove index 5. That removal shifts all of the indices. Now you call next() on the iterator, and you.. what? Go to index 11? Stay at index 10? The iterator has no way to know.
Similarly, if you add something to a collection while you're iterating over it (without using the iterator), the iterator doesn't know if that was added before or after the current index, so the next() function is broken.
This doesn't even get into data structures where the iterator order depends on what's in the collection, but the issues are similar to the ones I listed above.
But what I want to know is that what will happen if it would not throw a Exception and let the iteration and modification happened concurrently.
This is hypothetical because the respective (non-concurrent) collections don't work that way. If we hypothesize that they did allow "concurrent" modification, then we still cannot answer without making assumptions about how iteration would then be implemented. Finally, assuming that we just removed the fast-fail tests, then the behaviour will be collection specific.
Looking at your analysis for the HashMap case, you have to consider the internal state of the iterator object. I haven't looked at any specific implementation code, but a typical HashMap iterator will have an index for a hash chain in the main hash array, and a pointer to a node within the hash chain:
A Map.remove won't change the hashmap size, so the chain index won't be invalidated. However, if the wrong entry was removed, we could find that the iterator's node pointer could refer to a node that is no longer in the chain. This could cause the iteration to return deleted map entries.
You are correct that a Map.put that triggered a resize could cause the entries to be redistributed. This could cause some entries to be skipped, and others to be returned twice.
Usually the traditional collection classes in java.util package uses an int variable (modCount) to keep track of modifications (additions and deletions).
When we ask for an Iterator from these collection classes then a object of Iterator which is returned is provided with the existing modification count variable as its expected modification count.
Upon invoking the next() method the Iterator object checks the current modification count variable value against its expected modification count value.
In case of a mismatch it fails fast by throwing ConcurrentModificationException present in java.util package, its a RuntimeException.
Do not get confused between the size of the collection object (as in your question Map) and the total buckets available. Moreover its not about the size, one addition increases the value of the modification count flag and also a deletion increases its value.

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.

remove elements from CopyOnWriteArrayList

I am getting an exception when I try to remove elements from CopyOnWriteArrayList using an iterator.
I have noticed that it is documented
Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.
(from http://download.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html)
Now, surprisingly i can iterate it with foreach and use the remove() function . But then I get the famous bug - when trying to remove an item from a list using a for loop - you skip the element next to the removed element.
any suggestions then?
Iterate over the collection choosing all the elements you want to delete and putting those in a temporary collection. After you finish iteration remove all found elements from the original collection using method removeAll.
Would that work out for you? I mean, not sure if deletion logic is more complicated than that in your algorithm.
EDIT: I'm an idiot. I missed the fact that this is a copy-on-write list so every removal means a new copy. So my suggestions below are likely to be suboptimal if there's more than one removal.
Same as for any other list whose iterator doesn't support remove, or anything where you're not using an iterator. There are three basic techniques that come to mind to avoid this bug:
Decrement the index after removing something (being careful not to do anything with the index until the next iteration). For this you'll obviously have to use a for(int i=0; i < ... style of for loop, so that you can manipulate the index.
Somehow repeat what the inside of the loop is doing, without literally going back to the top of the loop. Bit of a hack - I would avoid this technique.
Iterate over the list in reverse (from end to start, instead of from start to end). I prefer this approach as it's the simplest.
Since this is a CopyOnWriteArrayList it is totally safe to remove elements while iterating with forEach. No need for fancy algorithms.
list.forEach(e -> {
if (shouldRemove(e))
list.remove(e);
});
EDIT: Well of course that works if you want to delete elements by reference, not by position.
Ususlly you would iterate first gathering elemenet to be deleted in a separate list then delete them outside the for each loop (which is disguised iterator based loop anyway)
Something like this:
int pos = 0;
while(pos < lst.size() ) {
Foo foo = lst.get(pos);
if( hasToBeRemoved(foo) ) {
lst.remove(pos);
// do not move position
} else {
pos++;
}
}
You could use Queue instead of List.
private Queue<Something> queue = new ConcurrentLinkedQueue<Something>();
It's thread safe and supports iterator.remove(). Be aware of the thread-safe behavior of Queue iterators, though (check the javadoc).
If you want to delete all use just clear(). If you want to keep elements put them in a temporary ArrayList and get them back from there.
List<Object> tKeepThese= new ArrayList<>();
for(ListIterator<Object> tIter = theCopyOnWriteArrayList; tIter.hasNext();)
{
tObject = tIter.next();
if(condition to keep element)
tKeepThese.add(tObject);
}
theCopyOnWriteArrayList.clear();
theCopyOnWriteArrayList.addAll(tKeepThese);
the shortest and most efficient way:
List<String> list = new CopyOnWriteArrayList<>();
list.removeIf(s -> s.length() < 1);
internally it creates an temporary array with the same length and copies all elements where the predicate returns true.
keep in mind that if you use this method to actually iterate over the elements to perform some action, these actions cannot be performed in paralell anymore since the removeIf-call is atomic and will lock the traversal for other threads
Below works fine with CopyOnWriteArrayList
for(String key : list) {
if (<some condition>) {
list.remove(key);
}
}

Categories