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.
Related
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 8 years ago.
I'm trying to remove some elements from an ArrayList while iterating it like this:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
Of course, I get a ConcurrentModificationException when trying to remove items from the list at the same time when iterating myArrayList. Is there some simple solution to solve this problem?
Use an Iterator and call remove():
Iterator<String> iter = myArrayList.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
As an alternative to everyone else's answers I've always done something like this:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
This will avoid you having to deal with the iterator directly, but requires another list. I've always preferred this route for whatever reason.
Java 8 user can do that: list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
It will remove elements in the list, for which someCondition is satisfied
You have to use the iterator's remove() method, which means no enhanced for loop:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
No, no, NO!
In single threated tasks you don't need to use Iterator, moreover, CopyOnWriteArrayList (due to performance hit).
Solution is much simpler: try to use canonical for loop instead of for-each loop.
According to Java copyright owners (some years ago Sun, now Oracle) for-each loop guide, it uses iterator to walk through collection and just hides it to make code looks better. But, unfortunately as we can see, it produced more problems than profits, otherwise this topic would not arise.
For example, this code will lead to java.util.ConcurrentModificationException when entering next iteration on modified ArrayList:
// process collection
for (SomeClass currElement: testList) {
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
}
}
But following code works just fine:
// process collection
for (int i = 0; i < testList.size(); i++) {
SomeClass currElement = testList.get(i);
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
i--; //to avoid skipping of shifted element
}
}
So, try to use indexing approach for iterating over collections and avoid for-each loop, as they are not equivalent!
For-each loop uses some internal iterators, which check collection modification and throw ConcurrentModificationException exception. To confirm this, take a closer look at the printed stack trace when using first example that I've posted:
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 TestFail.main(TestFail.java:43)
For multithreading use corresponding multitask approaches (like synchronized keyword).
While other suggested solutions work, If you really want the solution to be made thread safe you should replace ArrayList with CopyOnWriteArrayList
//List<String> s = new ArrayList<>(); //Will throw exception
List<String> s = new CopyOnWriteArrayList<>();
s.add("B");
Iterator<String> it = s.iterator();
s.add("A");
//Below removes only "B" from List
while (it.hasNext()) {
s.remove(it.next());
}
System.out.println(s);
If you want to modify your List during traversal, then you need to use the Iterator. And then you can use iterator.remove() to remove the elements during traversal.
List myArrayList = Collections.synchronizedList(new ArrayList());
//add your elements
myArrayList.add();
myArrayList.add();
myArrayList.add();
synchronized(myArrayList) {
Iterator i = myArrayList.iterator();
while (i.hasNext()){
Object object = i.next();
}
}
One alternative method is convert your List to array, iterate them and remove them directly from the List based on your logic.
List<String> myList = new ArrayList<String>(); // You can use either list or set
myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");
Object[] obj = myList.toArray();
for(Object o:obj) {
if(condition)
myList.remove(o.toString());
}
You can use the iterator remove() function to remove the object from underlying collection object. But in this case you can remove the same object and not any other object from the list.
from here
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 the following code:
Map<String, List<String>> map;
for(String k : map.keySet()){
List<String> list = map.get(k);
boolean empty = list.isEmpty();//CME
if(!empty && somecheck(k, ...)){
list.clear();
}
}
And I'm getting ConcurrentModificationException in isEmpty() method. List is an ArrayList. There is no other threads modifying list, because it was created in this method before (and the all map too).
The only place modifying list is clear(), but it called after isEmpty() and loop cannot execute on one list twice.
I'm using java 1.7
java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1169)
at java.util.ArrayList$SubList.size(ArrayList.java:998)
at java.util.AbstractCollection.isEmpty(AbstractCollection.java:86)
From the stacktrace you have given it appears the exception is being thrown in the sub-class implementing the SubList functionality - I assume the lists in your map are actually sublists of another list?
Presumably what is happening is that you are modifying the underlying list after you have created the sublist view (remember, a sublist is just a view onto another list - it does not take an independent copy).
Instead of putting sublists into the map, try taking a copy instead, for example:
map.put(key, new ArrayList(originalList.subList(start, end));
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 7 years ago.
I've stumbled upon this code, which throws a ConcurrentModificationException
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (String s : list) {
if (s.equals("a"))
list.remove(s);
}
If you add an Iterator and use a while-loop, the code works fine:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String s = iter.next();
if (s.equals("a")) {
iter.remove();
}
}
I dont understand, why it is necessary, to use Iterator<String> in this case.
Is it because of the ArrayList not having some sort of ability to get iterated, altough it is a subclass of Collection?
Is it necessary to use the while-loop, or could you produce a version with a for-loop?
If a Collection is modified while iterating over it, in most of the implementations, a ConcurrentModificationException is thrown.
The "foreach" version:
for (String s : list) {
if (s.equals("a"))
list.remove(s);
}
internally is equivalent to
for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
String s = i.next();
if (s.equals("a"))
list.remove(s);
}
As you can see, an interator is created, but the list is modified directly. The list can only be modified by the iterator i used to iterate over it.
Your link actually explains it pretty well:
In the foreach loop, compiler will make the .next() called after the operation of removing element, which caused the ConcurrentModificationException.
The for uses an iterator internally. But you delete on the list directly => CME. The internal call to next after the deletion will throw it because it finds the list modified.
In the example with while, you delete through the iterator, which works fine.
When removing object from collections, use ITERATORS
Your code does not work because you are modifying the collection while looping over it. According to Oracle's javadoc the only safe way to do it is using iterators.
Note that Iterator.remove() is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.
Have a look at this for further infos
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());