Why does calling remove() on an iterator give a ConcurrentModificationException? - java

I am trying to write a very simple method to remove duplicates in a LinkedList:
I try to do this without using additional buffer, so I maintain two iterators on the linked list, one does the normal iteration, and another iterates through all prior nodes to check for dupes (as indicated in CareerCup); however, the compiler tells me there is a CME even though I am calling itr1.remove():
public static void RemoveWithoutBuffer(LinkedList l) {
ListIterator itr1 = l.listIterator();
int count1 = 0;
int count2 = 0;
while (itr1.hasNext()) {
Object next = itr1.next();
count1++;
count2 = 0;
ListIterator itr2 = l.listIterator();
while (itr2.hasNext()) {
count2++;
if (count2 == count1)
break;
if (itr2.next() == next){
itr1.remove();
}
}
}
}
Another simpler solution of this problem with the aid of hashset is easy as follows, and no exception reported:
public static void Remove(LinkedList l) {
HashSet set = new HashSet();
ListIterator itr = l.listIterator();
while (itr.hasNext()) {
Object next = itr.next();
if (set.contains(next))
itr.remove();
else
set.add(next);
}
}
Is it because when I am iterating through itr2 I cannot modify on itr1? Is there a way to fix this? Thank you guys.

In the first case yes - you're altering the collection's contents via iterator2, while iterator1 is not aware about the changes. In the second case HashSet/HashMap don't allow removing elements while iterating through them.
You can add removed elements to another collection, and removeAll them after an iteration. E.g.
List toRemove = new ArrayList();
for (Object next : collection) {
if (someCondition) toRemove.add(next);
}
collection.removeAll(toRemove);
I hope it helps.
P.S. more details on how to remove elements from list, concerning algorithm complexity you can read here Removing ArrayList object issue

From the API docs:
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.

You are getting CME because the list is modified by second iterator and first iterator is not aware of that change. When next time it tries to access the list, it is already modified. And hence it throws the exception.
Please use only one iterator to modify list at a time.

Yes. You can think of it this way: when you create an iterator it gets the list's current "modification count". When an iterator removes an element from the list, it checks the modification count to see if it is what it expects, and if all is okay, it removes the element and updates the modification count on both the iterator and the list. The other iterator will still have the old modification count and see the new value and throw the CME.
The hashset based way is the right solution in most cases. It will perform much better--two O(n) passes is usually better than O(n^2) as the nested iteration solution would produce (if it worked).

The reason why you get CME was explained by others, here is possible way you can use to remove dups
Use a List toRemove to record element at the first time iterator stumble into it, afterwards when meet again with the recorded element, remove it using iterator.remove()
private void removeDups(List list) {
List toRemove = new ArrayList();
for(Iterator it = list.iterator(); it.hasNext();) {
Object next = it.next();
if(!toRemove.contains(next)) {
toRemove.add(next);
} else {
it.remove();
}
}
toremove.clear();
}

Yes, you can fix this without using extra space.
The problem comes from executing these two lines, one after another.
itr1.remove();
itr2.hasNext();
You can use two iterators over the same list at the same time. If you are careful.
But once one of those iterators has modified the list (as it happens with itr1.remove() ) you can no longer use the other iterator (so you can't call itr2.hasNext()).
The solution is to put a break after itr1.remove(). And to update count1 :
public static void RemoveWithoutBuffer(LinkedList l) {
ListIterator itr1 = l.listIterator();
int count1 = 0;
int count2 = 0;
while (itr1.hasNext()) {
Object next = itr1.next();
count1++;
count2 = 0;
ListIterator itr2 = l.listIterator();
while (itr2.hasNext()) {
count2++;
if (count2 == count1)
break;
if (itr2.next() == next){
itr1.remove();
--count1;
break;
}
}
}
}
A more elegant solution would be to compare with elements after the current one rather than elements before the current one :
public static void RemoveWithoutBuffer(LinkedList l) {
ListIterator itr1 = l.listIterator();
while (itr1.hasNext()) {
Object next = itr1.next();
ListIterator itr2 = l.listIterator( itr1.nextIndex() );
while (itr2.hasNext()) {
if (itr2.next() == next) {
itr1.remove();
break;
}
}
}
}
These are not the best solutions in terms of computational complexity. If memory space is not an issue the hashset solution is a better one regarding computational complexity.
But the question is about concurrent mofication through iterators and not about complexity optimization.

Related

Using only nested loops, if-else statements, and ArrayList methods, how do I locate and remove duplicate values in an ArrayList (Java)? [duplicate]

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

Removing elements from array in java throws exception [duplicate]

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

Removing element from list in counted loop vs iterator [duplicate]

This question already has answers here:
Why iterator.remove does not throw ConcurrentModificationException
(6 answers)
Closed 7 years ago.
Why is this legal:
for(int i=0; i < arr.size(); i++) {
arr.remove(i);
}
But using an iterator or the syntactic sugar of a for each results in a ConcurrentModificationException:
for(String myString : arr) {
arr.remove(myString);
}
Before everyone starts jumping on the bandwagon telling me to use iterator.remove(); I'm asking why the different behavior, not how to avoid the conc mod exception. Thanks.
Let's take a look at how, e.g., ArrayLists's iterator is implemented:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
public E next() {
checkForComodification();
int i = cursor;
if (i >= size) throw new NoSuchElementException();
// ...
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
// ...
ArrayList.this.remove(lastRet);
// ...
cursor = lastRet;
lastRet = -1;
}
Let's look at an example:
List list = new ArrayList(Arrays.asList(1, 2, 3, 4));
Iterator it = list.iterator();
Integer item = it.next();
We remove the first element
list.remove(0);
If we want to call it.remove() now, the iterator would remove number 2 because that's what field lastRet points to now.
if (item == 1) {
it.remove(); // list contains 3, 4
}
This would be incorrect behavior! The contract of the iterator states that remove() deletes the last element returned by next() but it couldn't hold its contract in the presence of concurrent modifications. Therefore it chooses to be on the safe side and throw an exception.
The situation may be even more complex for other collections. If you modify a HashMap, it may grow or shrink as needed. At that time, elements would fall to different buckets and an iterator keeping pointer to a bucket before rehashing would be completely lost.
Notice that iterator.remove() doesn't throw an exception by itself because it is able to update both the internal state of itself and the collection. Calling remove() on two iterators of the same instance collection would throw, however, because it would leave one of the iterators in an inconsistent state.
Looking at your code, I am assuming arr is a List. In the top loop you operate on the list directly, and "re-calibrate" your condition at the top when you check
i < arr.size()
So if you remove an element, i has to compare to a lesser value.
On the other hand, in the second case you operate on the collection after an iterator has been instantiated, and don't really re-calibrate yourself.
Hope this helps.
In the first one you are modifying an array that it's not being used as an iterator on your for loop.
In the second one you are trying to access to an array that it's being modified at the same time you are iterating with it on the loop. It's why it throws ConcurrentModificationException.

Old for-each loop works, new for-each loop gets ConcurrentModificationException

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

Remove all occurrencies of the first element in ArrayList

I want to remove all elements in ArrayList that are duplicates of the first element, but I want the firs element to remain in the ArrayList. I tried to do that with for loop, but it didn't remove all duplicates.
for(int i = 1; i < arraylist.size(); i++) {
if(arraylist.get(i) == v1)
arraylist.remove(i);
}
v1 is equal to the first element of the arraylist.
I also tried with ListIterator, but it removed the first element
ListIterator<Integer> iterator = arraylist.listIterator();
while(iterator.hasNext()) {
if(iterator.next().intValue() == v1)
iterator.remove();
}
Can you please help me?
You need to read the first element separately, outside the while loop, and store it in some variable, with which you would compare the rest of the elements, to remove:
ListIterator<Integer> iterator = arraylist.listIterator();
int first = 0;
// Check if there is a first element
if (iterator.hasNext()) {
first = iterator.next();
// Iterate over the rest of the elements
while(iterator.hasNext()) {
// If this value is equal to `first`, remove it
if(iterator.next().intValue() == first) {
iterator.remove();
}
}
}
System.out.println(arrayList);
iterator.next() will return a value of type Integer. Using intValue() will give your primitive value out.
But since I'm doing the comparison with an int primitive itself, you won't need to call intValue() at all. Your Integer will automatically be unboxed to primitive int before comparison. So, replacing the if statement in while with the below one will also work:
if(iterator.next() == first) {
iterator.remove();
}
As far as your first way is concerned, I would say, always use Iterator if you want to modify the List you are looping upon. This will prevent you from facing awkward ConcurrentModificationException.
See also:
Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop
You are doing the correct way by using Iterator and its method remove. But you should add a call to next() before the loop itself, in order to go over the first element and not remove it.
ListIterator<Integer> iterator = arraylist.listIterator();
iterator.next(); // pass the first element.
while(iterator.hasNext()) {
if(iterator.next().intValue() == v1)
iterator.remove();
}
Contrary to what others have said, you don't have to use "equals" if v1 is an int, which seems to be the case.
Count down (not up):
Object v1 = arraylist.get(0);
for (int i = arraylist.size() - 1; i > 1; i--) {
if (arraylist.get(i).equals(v1))
arraylist.remove(i);
}
You have to count down because as you remove elements, they the remaining ones shuffled down.
Also, you should change == to .equals() (as shown).
int v1 = arraylist.get(0);
for(int i = 0; i < arraylist.size(); i++) {
if(arraylist.get(i) == v1){
arraylist.remove(i);
i--;
}
}
If you don't want to use the Iterator approach: The other answers are correct in that you need to index from zero (if you want to remove the first one as well), but you also need to decrement your iteration variable (the i-- line) each time you remove an element from the list, because you're changing the list's length with remove().
It's best to be careful about removing elements from an array (or an iterable list) whilst iterating over it.
Easiest approach, in my experience, is to create a new list. Can you consider doing that?
Looking at your code, firstly remember to use "equals" over "==" for comparison (since .equals means "meaningfully equivalent", which I think is what you need here). (edit: may not matter here due to autoboxing, but it's still a nice habit to have)
But even this won't work:
for (int i = 1; i < arraylist.size(); i++) {
if (arraylist.get(i).equals(v1))
arraylist.remove(i);
}
Since imagine you have an ArrayList of three integers, all the same. When i == 1, The element at index 1 is compared to the value at index 0, and removed. But then the element at index 2 becomes the element at index 1, the for-loop counter is incremented, thereby "missing" to remove the last entry in the list.
Can I recommend something like this?
List<Integer> newlist = new ArrayList<Integer>();
newlist.add(v1);
for (Integer integer : arraylist) {
if (!integer.equals(v1))
newlist.add(integer);
}
Best of luck!
P.S. if you're feeling brave, you might be able to do a neat one-liner out of this:
CollectionUtils.filter(Collection,Predicate)
CollectionUtils.filter(arraylist.subList(1, arraylist.size()), new Predicate() {
#Override
public boolean evaluate(Object o) {
return !v1.equals(o);
}
});

Categories