I have seen in many places that it says when removing an element from an ArrayList while iterating, I should be using iterator remove method instead of collections remove method to avoid concurrent modification exception.
However, below code works fine using Java 1.8 Collection remove without giving concurrent modification exception. You can see that I am not using iterator here to remove the object.
List<MyObject> list = new ArrayList<MyObject>();
list.add(new MyObject());
list.add(new MyObject());
list.add(new MyObject());
for (int i=0; i<list.size(); i++) {
list.remove(i);
}
Your example will not throw an exception. It also won't remove all elements from the list, though.
What it really does
On first iteration, it removes index 0; 2 elements remain in the list
On second iteration, it removes index 1 (last element in fact); 1 element remains
Third iteration won't happen, because by then list.size() is 1 which is less than 2
At first, I thought it would throw an IndexOutOfBoundsException, but the fact that list.size() is evaluated at the start of each iteration ensures that it won't.
Why it doesn't throw ConcurrentModificationException
Because you're not using an iterator.
This exception is thrown if you iterate the list using an iterator, then modify (e.g. delete from) the list, and then try to advance the iterator, like this:
Iterator<MyObject> it = list.iterator();
while (it.hasNext()) {
list.remove(it.next());
}
As #Ferrybig noted, even in such case the iterator might not actually throw an exception - see related question.
Related
This question already has answers here:
Why am I not getting a java.util.ConcurrentModificationException in this example?
(10 answers)
Closed 8 years ago.
I'm trying to delete an element from an ArrayList inside a loop.
This is OK.
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3));
for(Integer i: list){
if(i == 2)
list.remove(i);
}
But this is not, and throw concurrentMOdificationException.
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3));
for(Integer i: list){
list.remove(i);
}
I don't understand why.
I just added another element, it is not OK either (throw concurrentMOdificationException).
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4));
System.out.println(list);
for (Integer i : list) {
if (i == 2)
list.remove(i);
}
Use the Iterator class instead of the for-each loop.
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer i = it.next();
it.remove();
}
http://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html
For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances. Some Iterator implementations (including those of all the general purpose collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected. Iterators that do this are known as fail-fast iterators, as they fail quickly and cleanly, rather that risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
You have to understand a little bit about what is going when use a for loop of this nature. It is really using a java.util.Iterator under the hood. This iterator will use the next() method to determine when iteration should stop, and the hasNext() method to retrieve the next element.
The kicker is that only next() checks for concurrent modification - hasNext() does not perform the check. In the first case, you wait until the second loop iteration to remove 2 from the list, and the next iteration finds the end of the list and exits. In the second case, you remove the 1 from the list during the first iteration, and the next iteration throws the exception when it tries to retrieve the next element.
Java is throwing ConcurrentModificationException when I am running the following code. Any idea why is that?
ArrayList<String> list1 = new ArrayList<String>();
list1.add("Hello");
list1.add("World");
list1.add("Good Evening");
for (String s : list1){
list1.remove(2);
System.out.println(s);
}
If you take a look at documentation of ConcurrentModificationException you will find that
This exception may be thrown by methods that have detected concurrent
modification of an object when such modification is not permissible.
For example, it is not generally permissible for one thread to modify
a Collection while another thread is iterating over it
...
Note that this exception does not always indicate that an object has
been concurrently modified by a different thread. If a single thread
issues a sequence of method invocations that violates the contract of
an object, the object may throw this exception. For example, if a
thread modifies a collection directly while it is iterating over the
collection with a fail-fast iterator, the iterator will throw this
exception.
Important thing about this exception is that we can't guarantee it will always be thrown as stated in documentation
Note that fail-fast behavior cannot be guaranteed as it is, generally
speaking, impossible to make any hard guarantees in the presence of
unsynchronized concurrent modification. Fail-fast operations throw
ConcurrentModificationException on a best-effort basis.
Also from ArrayList documentation
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.
(emphasis mine)
So you can't manipulate content of Collection (in your case List) while iterating over it via enhanced for loop because you are not doing it via iterator for-each is using internally.
To solve it just get your own Iterator and use it in your loop. To remove elements from collection use remove like in this example
Iterator<String> it = list1.iterator();
int i=0;
while(it.hasNext()){
String s = it.next();
i++;
if (i==2){
it.remove();
System.out.println("removed: "+ s);
}
}
form the doc you can read
This exception may be thrown by methods that have detected concurrent
modification of an object when such modification is not permissible.
For example, it is not generally permissible for one thread to modify
a Collection while another thread is iterating over it. In general,
the results of the iteration are undefined under these circumstances.
Some Iterator implementations (including those of all the general
purpose collection implementations provided by the JRE) may choose to
throw this exception if this behavior is detected. Iterators that do
this are known as fail-fast iterators, as they fail quickly and
cleanly, rather that risking arbitrary, non-deterministic behavior at
an undetermined time in the future.
further more, suppose that you can remove item 2 at each iteration. You will end up in a index out of bound exception:
at first iteration you remove item #2 ("Good evening") and list size become 1 (item 0 "hello" and item 1 "World")
at next iteration you remove item #2 which actually does not exist in your list. Your list is indeed of size two, but counter starts from 0, thus you end up removing something which is not there: this is an example of the non-deterministic behavior.
You cann't iterating over an list after the underlying list is modified.if you do that its give you ConcurrentModificationException
to resolve this issue use java.util.ListIterator for iteration of list.
ListIterator<String> it = list1.listIterator();
for (String s : list1) {
if (it.hasNext()) {
String item = it.next();
System.out.println(item);
}
}
You can't delete an item with ArrayList's remove() method while iterating over it. Use Iterator if you want to delete the item also while iterating.
But you are deleting an item based on index then simply moving below line outside the loop will solve your problem.
list1.remove(2);
The following code crashes(Concurrent modification exception) in JVM but not in DVM, i am unable to understand why its not crashing in andorid. I am using JB
List<String> list = new ArrayList<String>();
list.add("Something");//add only 1 object
for(String a:list){
list.remove(a);
}
You are iterating the list as well as trying to remove the value from the same list. This causes the Exception. Try adding the items to be removed in a separate list and after the iteration use List.removeall() function to remove all the unwanted values.
Sample:
List<String> removeList = new ArrayList<String>();
for(String a:list) {
removeList.add(a);
}
list.removeAll(removeList);
Hi yes it will give an exception as
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at coffee.CoffeeApp.main(CoffeeApp.java:20)
Java Result: 1
because
fail-fast behavior is implemented by keeping a modification count and if iteration thread realizes the change in modification count it throws ConcurrentModificationException.
so do something like this and iterate over list with the help of Iterator
List<String> list = new ArrayList<String>();
list.add("Something");//add only 1 object
Iterator<String> it = list.iterator();
while(it.hasNext()) {
it.next();
it.remove();
//Do something with obj
}
what we can say is the fail-fast behavior of an iterator cannot be guaranteed as it is, here an unsynchronized concurrent modification is there. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, fail-fast Iterators fail as soon as they realized that structure of Collection has been modified since iteration has begun and if the iterator thread realizes the modification ,then it throws ConcurrentModificationException. Changes can be additions, updation or deletion.
In android may be it is under different implementation so not having a problem.
for a comparison study please visit however i did not had a great R&D here
http://www.docjar.com/html/api/java/util/ArrayList.java.html
v/s
https://android.googlesource.com/platform/libcore/+/f33eae7e84eb6d3b0f4e86b59605bb3de73009f3/luni/src/main/java/java/util/ArrayList.java
While you're iterating over a List (or any Collection, for that matter), you can only modify the List through the very same Iterator. So this will work:
for(Iterator<String> listIter = list.iterator(); listIter.hasNext();) {
listIter.next().remove();
}
This question already has answers here:
Why is a ConcurrentModificationException thrown and how to debug it
(8 answers)
Closed 3 years ago.
Why does the following code throw ConcurrentModificationExcrption, when I clear the sub List after the master List, but not if I clear the sub list and then the master List?
ArrayList<Integer> masterList = new ArrayList<Integer>();
List<Integer> subList;
// Add some values to the masterList
for (int i = 0; i < 10; i++) {
masterList.add(i * i);
}
// Extract a subList from the masterList
subList = masterList.subList(5, masterList.size() - 1);
// The below throws ConcurrentModificationException
masterList.clear();
subList.clear(); // Exception thrown in this line
// The below doesn't throw any exception
subList.clear();
masterList.clear(); // No exception thrown. Confused??
SubList is not an independent entity, but it is just giving a view of the original list, and internally refers to same list. Hence, its design seem to be such that if underlying list is modified structurally (addition/removal of elements), it is not able to fulfill its contract.
As can be seen here in the source code of SubList, the method checkForComodification checks whether the underlying list has been modified, and thus if the modCount (number of times the list has been structurally modified) value of SubList is not same as parent ArrayList, then, it throws ConcurrentModificationException
So, clearing parent ArrayList from which SubList was created can result in the certain operations of SubList to result in ConcurrentModificationException
subList is a view over the masterList. There is just 1 underlying collection. Now masterList is kind of a superset of sublist. So,
sublist cannot exist if masterlist's elements are removed //exception case
masterlist can exist if sublist's elements are removed //OK
acording to ArrayList doc subList() returns a sublist that is backed by the original ArrayList, so if the original changes so does the subList, when you execute subList.clear() the sublist itself doesn't exist anymore.
From the API docs:
The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)
Undefined semantics means of course that it is allowed to throw an exception (and indeed this is probably the wisest course of action).
So you can change the size of the sublist and have those changes reflected in the main list, but the reverse isn't true.
This is my code:
Iterator it = queue.iterator();
while(it.hasNext()){
random = randNumber(1,2);
if(random == 1){
queue.poll();
} else {
queue.add("new");
queue.poll();
}
}
It gives me:
Exception in thread "test" java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:761)
at java.util.LinkedList$ListItr.next(LinkedList.java:696)
Edit #Jon Skeet:
What I want to do is:
I have a queue list in, let say the size is 10, lets say: a,b,c,d ... j
Generate a number between 1 and 2. if 1, pull (remove the top element) else if 2 add new element
I will stop the loop until I added 3 new elements
In general, you can't modify collections while you're iterating over them. One alternative is to build a separate list of "changes" you want to apply, and then apply them once you've finished iterating.
Alternatively, some collections do support this such as ConcurrentLinkedQueue - but most offer no guarantees about whether the iterator will see the changes made while you're iterating. (I suspect that's mainly because they're also thread-safe, but I've rarely seen documented guarantees about what will happen if you modify the collection within the iterating thread.)
EDIT: I'm not sure that an iterator is the right approach here. Instead, you could use:
while (!queue.isEmpty())
{
// Put logic in here - add, poll etc
}
One thing to note is that your posted code doesn't actually move the iterator forward at any time - it never calls it.next(). That's a strong suggestion that either you're not using the iterator fully, or you don't need it at all.
Is this really copy-pasted your code?
What I can think is that You confirmed that q and queue are references to the same queue-object.
So you cannot modify any type of list, queue, stack when iterating over it. To avoid iteration, you can try a for(int i = 0; ..... ; i++) loop. But this isn't a good idea. Because you are changing size when you are using the size to check if you are at the end of the loop.
I read your edit:
for (int i = 0; i < 3;)
{
boolean b = Math.random() < 0.5d;
if (b)
{
queue.poll();
} else {
queue.add("new"); // or put(), I don't know exectly
i++;
}
}
If 'queue' is a List, you can use ListIterator
excerpt:
An iterator for lists that allows the programmer to traverse the list in either direction, modify the list during iteration, and obtain the iterator's current position in the list.
You should use ListIterator. It has .remove() and .set() methods that will allow you to modify the underlying collection without hosing the iterator's state.
In general, modifying the collection in any other way is going to hose the iterator's state--be glad it threw an exception and didn't do something more insidious.