This question already has answers here:
Why is a ConcurrentModificationException thrown and how to debug it
(8 answers)
Closed 3 years ago.
I have below two java class
import java.util.*;
public class ArrayListTest032 {
public static void main(String[] ar) {
List<String> list = new ArrayList<String>();
list.add("core java");
list.add("php");
list.add("j2ee");
list.add("struts");
list.add("hibernate");
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
list.remove("php");
while (itr.hasNext()) {
System.out.println(itr.next());
}
}
}
When I run above code I get below output.
core java
php
j2ee
struts
hibernate
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at ArrayListTest032.main(ArrayListTest032.java:20)
Which is expected as I am modifying the list while iterating. But in below java class same logic is executed by set family.
import java.util.*;
public class HashSetTest021 {
public static void main(String[] ar) {
Set<String> set = new HashSet<String>();
set.add("core java");
set.add("php");
set.add("j2ee");
set.add("struts");
set.add("hibernate");
Iterator<String> itr = set.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
set.remove("php");
while (itr.hasNext()) {
System.out.println(itr.next());
}
}
}
And out put is.
hibernate
core java
j2ee
php
struts
There is no any ConcurrentModificationException.
I just want to know why same piece of code throws ConcurrentModificationException in case of list family, but there is no any ConcurrentModificationException in case of set family
This is a kind of 'retrograde' behavior, insomuch as iterators, once fully traversed, are not reusable, aka their hasNext method should return false when you reach the end of the list.
In this case though, the iterator returned by ArrayList.iterator is an internal implementation class, with code for hasNext as follows:
public boolean hasNext() {
return cursor != size;
}
So when you call hasNext in your second loop, it indicates (falsely) that there are more items to iterate over, because you executed an operation that changed the size of the list, after the first iteration. Semantically, you should not be able to continue iterating over items in the list after you reach the end of it, but due to this implementation detail it lets you proceed with the second while loop. Of course, at that point, you get a concurrent modification exception because of the change you made in the backing list.
On the other hand, the iterator used by your hash set has its hasNext implemented as follows:
public final boolean hasNext() {
return next != null;
}
This implementation happens not to be as 'vulnerable' to modifications made to the hash set after an iteration has been completed, and as such the hasNext method is better behaved.
This is a difference in the implementation: the iterator returned by the array list detects concurrent modifications even when it is positioned at the end, because it checks the length; iterators of the HashSet, TreeSet and LinkedList, on the other hand, do not detect this condition, because they check for being positioned at the end before checking for concurrent modification. The documentation allows iterators not to throw on concurrent modifications, so both approaches are valid.
Demo for the TreeSet.
Demo for the LinkedList.
Start by reading the JavaDoc for Iterator. Does it mention ConcurrentModificationException anywhere?
Now, read the JavaDoc for ConcurrentModificationException, and note the following (emphasis added):
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
Now look closely at your code. Your while loop iterates through all elements of the collection (even though the output of your first example doesn't indicate this, which tells me that either you've edited the output or this is not your actual code). At the time you remove the element, there are no more items to iterate, so the second loop should always exit immediately.
So, the conclusion is that the implementers of the list iterator have chosen that to throw that exception even when there are no more elements to iterate, while the implementers of the set iterator have chosen not to. Both cases are completely acceptable given the specifications.
public static void main(String[] ar) {
List<String> list = new ArrayList<String>();
list.add("core java");
list.add("php");
list.add("j2ee");
list.add("struts");
list.add("hibernate");
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
list.remove("php");
/* while (itr.hasNext()) {
System.out.println(itr.next());
}*/
}
problem in itr object.it holds the list object reference
Hashset can throw a ConcurrentModificationException if you do anything to the set except through the iterator. However, there are a lot of heuristics around the itertor's fast-fail behavior with the goal to complete the iteration if at all possible. The JavaDocs seem pretty clear on it's behavior.
In case of list when we traverse it with first loop
Iterator itr = set.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
The cursor value and size will become same.Cursor contains value for total no of elements traversed and inside hashNext() method for list traversal contains code as:
public boolean hasNext() {
return cursor != size;
}
So after first while loop cursor == size.But after removing element from list size becomes (originalSize-1).So for next while loop it goes inside while and inside itr.next() method it checks for modcount modification and throws ConcurrentModificationException.
In case of Set it checks for next != null for every itr.hasnext() call.And after traversing first while loop next becomes null.Removing element from set does not affect next value as null and itr.hasNext will return next == null as true and hence it does not go inside while loop to check modcount modification.And hence it does not throw ConcurrentModification Exception.
Related
I am having the following problem:
Given an ArrayList (let's call it list), how can I "double-iterate" through it without getting ConcurrentModificationException?
Here's what I've tried:
iterator out = list.iterator();
iterator in;
while(out.hasNext()){
...
in = list.iterator();
while(in.hasNext()){
...
if(something)
in.remove();
}
You can't do that. A potential solution might be to mark objects to be removed, for example:
final List<Foo> toRemove = new ArrayList<>();
for (Foo a : list)
{
for (Foo b : list)
{
if (something)
{
toRemove.add(b);
}
}
}
list.removeAll(toRemove);
You may need some additional checks to see that the object isn't already marked for removal. It's impossible to say given how vague your example is.
You are trying to modify an iterator. It will give you concurrentModification exception.
In java 8 you can easily remove it using
list.removeIf(someCondition)
Try this link java8 collections
The Iterator instance provided through a call to List#iterator method preserves a count scalar allowing to detect external changes to the Collection container.
When an element is removed from the collection by any other mean than going through the same Iterator#remove(T) call, the count is not updated behind the scenes.
Therefore when you request for the #next() element through the iterator instance, the count is checked against an expected value and if both values does not match (since an element has been removed through another iterator) a ConcurrentModificationException is thrown (even though you may be working in a single threaded environment).
The solution whould be, as #Michael stated, to keep track of the container elements that should be removed then perform a bulk delete:
Collection<Object> temp = new ArrayList<>();
iterator out = list.iterator();
iterator in;
while (out.hasNext()) {
// ...
in = list.iterator();
while (in.hasNext()) {
// ...
if(something)
// just mark the element for deletion
temp.add(in.next());
}
}
// delete all the obsolete elements
list.removeAll(temp);
In collection once iterator creator If you try to modify the content not through same iterator it will throw concurrent exception.If you required some special kind of iterator then you can go ahead and implement your own.
I have a code that adds data to a list. What I do not understand is why
the UnsupportedOperationException is thrown in one case and
ConcurrentModificationException in the other.
I am adding data in list in both the case and then trying to remove list
data while iterating over the list.
What i have read so far is that whenever any modification is made to
fail- fast collection,ConcurrentModificationException is thrown. So why this
different behavior in both these cases?
List<String> animalList = new ArrayList<>();
animalList.add("cat");
animalList.add("dog");
animalList.add("bear");
animalList.add("lion");
Iterator<String> iter = animalList.iterator();
while(iter.hasNext()){
String animal = iter.next();
System.out.println(animal);
animalList.remove(3);
}
This code throws ConcurrentModificationException
String[] strings = { "Java", "Honk", "Test" };
List<String> list = Arrays.asList(strings);
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String name = iterator.next();
System.out.println(name);
list.remove(3);
}
while this one throws UnsupportedOperationException
For the code block, where you get ConcurrentModificationException , you get that exception because you created an iterator on List then removing directly from list from within loop so iterator has issues. You should use remove() method of iterator itself - Iterator.remove().
You should directly remove an element from list when removing from outside iterator loop. See this another SO Question
In second case, with Arrays.asList , you get a List but actual list object might not be an ArrayList and remove(int index) operation is optional at List interface. See this
All in all, as far as UnsupportedOperationException is concerned, in first case you are guaranteed to working with an ArrayList and for that class, remove operation is supported , See this
For second case, you can't be so sure. Refer documentation of Arrays.asList where it says that returned list of fixed size so certain operations are not supported.
Arrays.asList does not return an ArrayList. In fact, the list returned is not modifiable, thus when you try to modify it, it throws the UnsupportedOperationException.
This question already has answers here:
Why is a ConcurrentModificationException thrown and how to debug it
(8 answers)
java.util.ConcurrentModificationException not thrown when expected
(2 answers)
Closed 3 years ago.
The following Java code throws a ConcurrentModificationException, as expected:
public class Evil
{
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("lalala");
c.add("sososo");
c.add("ahaaha");
removeLalala(c);
System.err.println(c);
}
private static void removeLalala(Collection<String> c)
{
for (Iterator<String> i = c.iterator(); i.hasNext();) {
String s = i.next();
if(s.equals("lalala")) {
c.remove(s);
}
}
}
}
But the following example, which differs only in the contents of the Collection, executes without any exception:
public class Evil {
public static void main(String[] args)
{
Collection<String> c = new ArrayList<String>();
c.add("lalala");
c.add("lalala");
removeLalala(c);
System.err.println(c);
}
private static void removeLalala(Collection<String> c) {
for (Iterator<String> i = c.iterator(); i.hasNext();) {
String s = i.next();
if(s.equals("lalala")) {
c.remove(s);
}
}
}
}
This prints the output "[lalala]". Why doesn't the second example throw a ConcurrentModificationException when the first example does?
Short answer
Because the fail-fast behavior of an iterator isn't guaranteed.
Long answer
You're getting this exception because you cannot manipulate a collection while iterating over it, except through the iterator.
Bad:
// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {
// here, the collection will check it hasn't been modified (in effort to fail fast)
String s = i.next();
if(s.equals("lalala")) {
// s is removed from the collection and the collection will take note it was modified
c.remove(s);
}
}
Good:
// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {
// here, the collection will check it hasn't been modified (in effort to fail fast)
String s = i.next();
if(s.equals("lalala")) {
// s is removed from the collection through iterator, so the iterator knows the collection changed and can resume the iteration
i.remove();
}
}
Now to the "why": In the code above, notice how the modification check is performed - the removal marks the collection as modified, and next iteration checks for any modifications and fails if it detects the collection changed. Another important thing is that ArrayList (not sure about other collections) does not check for modification in hasNext().
Therefore, two strange things may happen:
If you remove the last element while iterating, nothing will be thrown
That's because there's no "next" element, so the iteration ends before reaching the modification-checking code
If you remove the second-to-last element, ArrayList.hasNext() will actually also return false, because the iterator's current index is now pointing at the last element (former second-to-last).
So even in this case, there's no "next" element after the removal
Note that this all is in line with ArrayList's documentation:
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.
Edited to add:
This question provides some information on why the concurrent modification check is not performed in hasNext() and is only performed in next().
If you look at the source code for the ArrayList iterator (private nested class Itr), you'll see the flaw in the code.
The code is supposed to be fail-fast, which is done internally in the iterator by calling checkForComodification(), however the hasNext() doesn't make that call, likely for performance reasons.
The hasNext() instead is just:
public boolean hasNext() {
return cursor != size;
}
This means that when you're on the second last element of the list, and then remove an element (any element), the size is reduced and hasNext() thinks you're on the last element (which you weren't), and returns false, skipping the iteration of the last element without error.
OOPS!!!!
From other answers you know what is the right way of removing an element in collection while you are iterating the collection.
I give here the explanation to the basic question.And the answer to your question lies in the below stack trace
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.ii4sm.controller.Evil.removeLalala(Evil.java:23)
at com.ii4sm.controller.Evil.main(Evil.java:17)
In the stacktrace it is obvious that i.next(); line throws the error. But when you have only two elements in the collection.
Collection<String> c = new ArrayList<String>();
c.add("lalala");
c.add("lalala");
removeLalala(c);
System.err.println(c);
When the first one is removed i.hasNext() returns false and i.next() is never executed to throw the exception
you should remove from the iterator (i) not the collection (c) directly;
try this:
for (Iterator<String> i = c.iterator(); i.hasNext();) {
String s = i.next();
if(s.equals("lalala")) {
i.remove(); //note here, changing c to i with no parameter.
}
}
EDIT:
The reason why the first try throws exception while the second one doesn't is simply because of the number of element in your collection.
as the first one will go through the loop more than once and the second one will only iterate once. therefore, it doesn't have the chance to throw exception
You can't remove from list if you're browsing it with "for each" loop.
You can't remove an item from a collection you're iterating over. You can get around this by explicitly using an Iterator and removing the item there. You can use Iterator.
If you use below code you will not get any exception :
private static void removeLalala(Collection<String> c)
{
/*for (Iterator<String> i = c.iterator(); i.hasNext();) {
String s = i.next();
if(s.equals("lalala")) {
c.remove(s);
}
}*/
Iterator<String> it = c.iterator();
while (it.hasNext()) {
String st = it.next();
if (st.equals("lalala")) {
it.remove();
}
}
}
From what I've read I understand that you get a ConcurrentModificationException when you try to edit a list while it's still being iterated.
Now what I don't get is, why does the old foreach loop not give the exception while the the new foreach loop does?
public void newForeachLoop() {
for (Person person : list) {
if (person.getPosition().equals(this.getPosition())) {
list.remove(person);
}
}
}
public void oldForeachLoop() {
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getPosition().equals(this.getPosition())) {
list.remove(list.get(i));
}
}
}
In the old loop you're not using the lists iterator instead you're using a count of the objects in the list.
In the new loop you're using the built-in iterator which is a pointer for that instance. When you remove an item from the list you're modifying that instance and resetting the iterator thus throwing the exception.
Because for each loop is iterator based, you can't just remove an element from the list while iterating over it.
You can even try explicitly using iterator and removing an element.
List<String> list= new ArrayList <String>;
list.add("One");
list.add("two");
list.add("three");
Iterator listItr = list.iterator () ;
while ( listItr.hasNext() )
{
String countStr = itr.next();
if ( countStr.equals ("two"))
itr.remove(); //will not throw any exception
//if you do it list.remove (countStr) //will throw exception
}
Removing an element from list using index while iterating over it, will definitely not throw any exception but you need to be extra careful about its length getting modified. Even indexes of further elements are also disturbed by your operation. So if you take care of this its not a problem.
As #SacJn explained, you cannot make structural changes in the List (e.g. add or remove elements) while iterating it via iterator(). The iterator() will detect the inconsistency and throw a ConcurrentModificationException. In Java-8 there's clean and safe way to solve your task:
public void java8Solution() {
list.removeIf(person -> person.getPosition().equals(this.getPosition()));
}
I understand that this exception is occurring because I'm trying to modify a list while I'm iterating through a list, but I don't understand how to get around it.
for(Villager e : tasked)
{
if(!e.hasTask())
{
tasked.remove(e);
}
}
The code is pretty self-explanatory. If the villager "e" does not have a task, it should be removed from this list.
Use a ListIterator, which allows you to make changes to the list through which you are iterating.
The code you use is almost indentical in use to an iterator, but removing elements has to be done explicitly using the remove() method of the iterator.
e.g.
Iterator itr = tasked.iterator();
while(itr.hasNext()) {
Villager e = itr.next();
if(!e.hasTask()) {
itr.remove();
}
}
The ConcurrentModificationException is a RuntimeException that may be thrown by methods that have detected concurrent modification of an object, when such modification is not permissible. An example of not permissible behavior is when a thread tries to modify the internal structure of a Collection, while another thread is iterating over it.
Use Iterator's remove method.
Iterator<Village> villageItr = tasked.iterator();
while(villageItr.hasNext()){
Village e=villageItr.next();
if(!e.hasTask()){
//remove that element from collection
villageItr.remove();
}
}
Create separate list (e.g. itemsToRemove), that contains all items you want to remove and then use
tasked.removeAll(itemsToRemoveList)
e.g.
List<Villager> toRemove = new ArrayList();
for(Villager e : tasked)
{
if(!e.hasTask())
{
toRemove.add(e);
}
}
tasked.removeAll(toRemove);
The Only issue with this approach, if the 'toRemove' list size is very large, you can do the opposite 'ls.retainAll(c)', by only identify what you want to keep.
If you are using java8 you can filter the list using stream API instead of iterating over it by hand.
List<Villager> villagersWithTask = tasked.stream().filter(e -> e.hasTask()).collect(Collectors.toList());