Removing item from list while iterating - java

While iterating through a list, an item can possibly be removed.
private void removeMethod(Object remObj){
Iterator<?> it = list.iterator();
while (it.hasNext()) {
Object curObj= it.next();
if (curObj == remObj) {
it.remove();
break;
}
}
}
The problem for me occurs when the above code can take place in another loop, which is actively iterating the original list.
private void performChecks(){
for(Object obj : list){
//perform series of checks, which could result in removeMethod
//being called on a different object in the list, not the current one
}
}
How can I remove an unknown object from a list while traversing it?
Example
I have a list of listener objects. While notifying the listeners of an event, other listeners may no longer be needed.

If I understand your problem correctly, followings would be the possible solutions(might not be most effective but i think is worth a shot):
Under performChecks() use for(Object obj : list.toArray())
Advantage: every time the list is "refreshed" to array it will reflect the changes.
Therefore, if the item is removed from the list in the separate loop

Your question is a bit confusing so I'll answer what I think I understand; your question comes to this: how to remove an item from a list while the list is iterated concurrently and items are being removed OR how to avoid ConcurrentModificationException.
First, the problem in your code is that you remove the item with your iterator and not by the list. Second, if you're using concurrency, use the CopyOnWriteArrayList and remove the item with
list.remove()
to provide a good example for the scenario, check this
so this is not good:
List<String> myList = new ArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while(it.hasNext()){
String value = it.next();
System.out.println("List Value:"+value);
if(value.equals("3")) myList.remove(value);
}
and this is good:
List<String> myList = new CopyOnWriteArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while(it.hasNext()){
String value = it.next();
System.out.println("List Value:"+value);
if(value.equals("3")){
myList.remove("4");
myList.add("6");
myList.add("7");
}
}
System.out.println("List Size:"+myList.size());

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

Why ConcurrentModificationException in ArrayList? [duplicate]

This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 4 years ago.
why following code throwing ConcurrentModificationException? Josh Bloch can avoid ConcurrentModificationException.
ArrayList<Integer> list=new ArrayList<Integer>();
list.add(100);
list.add(200);
list.add(300);
list.add(400);
for(Integer field : list) {
list.remove(field);
list.add(200);
}
You can't use remove on the list while using the "for each" loop. Instead, you can use this to call remove on the iterator:
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()) {
Integer integer = iterator.next();
// ...check if you want to remove this one...
iterator.remove();
}
If you actually want to replace every value with "200", or replace with some other value, it might make more sense to build up a new list:
List<Integer> newList = new ArrayList<Integer>();
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()) {
Integer integer = iterator.next();
newList.add(integer);
iterator.remove();
}
It's unclear what the behavior should be if you're iterating over an array while modifying it.
What if you remove an element, should it still be iterated over?
Rather than trying to guess, the list throws a ConcurrentModificationException to cause an error rather than pass with unexpected behavior.
One solution is that you could iterate over a shallow copy of the list, and then modify the original list
You can remove objects from the ArrayList which you are using. I use this in my game engine and it works.
See http://code.google.com/p/game-engine-for-java/source/browse/src/com/gej/map/Map.java#350
for (int i = 0; i < objects.size(); i++) {
GObject other = objects.get(i);
if (other.isAlive()) {
// Update it
} else {
// Else remove it
objects.remove(i);
}
}
What the error you are having is this does not work for the for each loop. Try in a normal for loop and that should solve your problem.
Change your code to this.
ArrayList<Integer> list =new ArrayList<Integer>();
ArrayList<Integer> remove = new ArrayList<Integer>();
list.add(100);
list.add(200);
list.add(300);
list.add(400);
// Mark to remove
for (int i=0; i<list.size(); i++){
remove.add(list.get(i));
}
list.removeAll(remove);
remove.clear();
// adding 200 at the end because if added in the loop,
// it removes the 200 and adds every loop which causes
// unnecessary memory usage.
list.add(200);

Create List<T> instance from Iterator<T>

Anyone know if there is a standard way to create a List from an Iterator instance?
I tend towards Guava's Lists.newArrayList(Iterator) because I generally have Guava as a dependency, and it already exists.
Use the Iterator to get every element and add it to a List.
List<String> list = new LinkedList<String>();
while(iter.hasNext()) { // iter is of type Iterator<String>
list.add(iter.next());
}
I had this need, and as a user of Apache Commons, this is what I did:
IteratorUtils.toList(iterator);
Javadoc here: https://commons.apache.org/proper/commons-collections/javadocs/api-3.2.2/org/apache/commons/collections/IteratorUtils.html#toList(java.util.Iterator)
In Java 8 you can use these ways (while verbose enough):
Iterator<String> iterator = ...;
List<String> list = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
.collect(Collectors.toList());
or
Iterator<String> iterator = ...;
List<String> list = new ArrayList<>(); // or LinkedList() if more relevant (adding being faster for LinkedList)
iterator.forEachRemaining(list::add);
try something like the following:
public T List<T> listFromIterator(Iterator<T> iterator) {
List<T> result = new LinkedList<T>();
while(iterator.hasNext()) {
result.add(iterator.next());
}
}
One thing to note is that if the iterator is not at the beginning of your structure, you have no way of retrieving previous elements.
If you have the collection that the iterator is from, you can create a list by using a constructor that takes a collection. ex: the LinkedList constructor:
LinkedList(Collection<? extends E> c)
This is the way that I convert from List to Iterator and vice versa.
ArrayList arrayList = new ArrayList();
// add elements to the array list
arrayList.add("C");
arrayList.add("A");
arrayList.add("E");
arrayList.add("B");
arrayList.add("D");
arrayList.add("F");
// use iterator to display contents of arrayList
System.out.print("Original contents of arrayList: ");
Iterator iterator = arrayList.iterator();
ArrayList arrayList2 = new ArrayList();
while(iterator.hasNext()) {
Object element = iterator.next();
arrayList2.add(element);
System.out.print(element + " ");
}

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

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.

Categories