Let me give a few examples with some alternatives to avoid a ConcurrentModificationException.
Suppose we have the following collection of books
List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
Collect and Remove
The first technique consists in collecting all the objects that we want to delete (e.g. using an enhanced for loop) and after we finish iterating, we remove all found objects.
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);
This is supposing that the operation you want to do is "delete".
If you want to "add" this approach would also work, but I would assume you would iterate over a different collection to determine what elements you want to add to a second collection and then issue an addAll method at the end.
Using ListIterator
If you are working with lists, another technique consists in using a ListIterator which has support for removal and addition of items during the iteration itself.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
Again, I used the "remove" method in the example above which is what your question seemed to imply, but you may also use its add method to add new elements during iteration.
Using JDK >= 8
For those working with Java 8 or superior versions, there are a couple of other techniques you could use to take advantage of it.
You could use the new removeIf method in the Collection base class:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
Or use the new stream API:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
In this last case, to filter elements out of a collection, you reassign the original reference to the filtered collection (i.e. books = filtered) or used the filtered collection to removeAll the found elements from the original collection (i.e. books.removeAll(filtered)).
Use Sublist or Subset
There are other alternatives as well. If the list is sorted, and you want to remove consecutive elements you can create a sublist and then clear it:
books.subList(0,5).clear();
Since the sublist is backed by the original list this would be an efficient way of removing this subcollection of elements.
Something similar could be achieved with sorted sets using NavigableSet.subSet method, or any of the slicing methods offered there.
Considerations:
What method you use might depend on what you are intending to do
- The collect and
removeAltechnique works with any Collection (Collection, List, Set, etc). - The
ListIteratortechnique obviously only works with lists, provided that their givenListIteratorimplementation offers support for add and remove operations. - The
Iteratorapproach would work with any type of collection, but it only supports remove operations. - With the
ListIterator/Iteratorapproach the obvious advantage is not having to copy anything since we remove as we iterate. So, this is very efficient. - The JDK 8 streams example don't actually removed anything, but looked for the desired elements, and then we replaced the original collection reference with the new one, and let the old one be garbage collected. So, we iterate only once over the collection and that would be efficient.
- In the collect and
removeAllapproach the disadvantage is that we have to iterate twice. First we iterate in the foor-loop looking for an object that matches our removal criteria, and once we have found it, we ask to remove it from the original collection, which would imply a second iteration work to look for this item in order to remove it. - I think it is worth mentioning that the remove method of the
Iteratorinterface is marked as "optional" in Javadocs, which means that there could beIteratorimplementations that throwUnsupportedOperationExceptionif we invoke the remove method. As such, I'd say this approach is less safe than others if we cannot guarantee the iterator support for removal of elements.
Let me give a few examples with some alternatives to avoid a ConcurrentModificationException.
Suppose we have the following collection of books
List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
Collect and Remove
The first technique consists in collecting all the objects that we want to delete (e.g. using an enhanced for loop) and after we finish iterating, we remove all found objects.
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);
This is supposing that the operation you want to do is "delete".
If you want to "add" this approach would also work, but I would assume you would iterate over a different collection to determine what elements you want to add to a second collection and then issue an addAll method at the end.
Using ListIterator
If you are working with lists, another technique consists in using a ListIterator which has support for removal and addition of items during the iteration itself.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
Again, I used the "remove" method in the example above which is what your question seemed to imply, but you may also use its add method to add new elements during iteration.
Using JDK >= 8
For those working with Java 8 or superior versions, there are a couple of other techniques you could use to take advantage of it.
You could use the new removeIf method in the Collection base class:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
Or use the new stream API:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
In this last case, to filter elements out of a collection, you reassign the original reference to the filtered collection (i.e. books = filtered) or used the filtered collection to removeAll the found elements from the original collection (i.e. books.removeAll(filtered)).
Use Sublist or Subset
There are other alternatives as well. If the list is sorted, and you want to remove consecutive elements you can create a sublist and then clear it:
books.subList(0,5).clear();
Since the sublist is backed by the original list this would be an efficient way of removing this subcollection of elements.
Something similar could be achieved with sorted sets using NavigableSet.subSet method, or any of the slicing methods offered there.
Considerations:
What method you use might depend on what you are intending to do
- The collect and
removeAltechnique works with any Collection (Collection, List, Set, etc). - The
ListIteratortechnique obviously only works with lists, provided that their givenListIteratorimplementation offers support for add and remove operations. - The
Iteratorapproach would work with any type of collection, but it only supports remove operations. - With the
ListIterator/Iteratorapproach the obvious advantage is not having to copy anything since we remove as we iterate. So, this is very efficient. - The JDK 8 streams example don't actually removed anything, but looked for the desired elements, and then we replaced the original collection reference with the new one, and let the old one be garbage collected. So, we iterate only once over the collection and that would be efficient.
- In the collect and
removeAllapproach the disadvantage is that we have to iterate twice. First we iterate in the foor-loop looking for an object that matches our removal criteria, and once we have found it, we ask to remove it from the original collection, which would imply a second iteration work to look for this item in order to remove it. - I think it is worth mentioning that the remove method of the
Iteratorinterface is marked as "optional" in Javadocs, which means that there could beIteratorimplementations that throwUnsupportedOperationExceptionif we invoke the remove method. As such, I'd say this approach is less safe than others if we cannot guarantee the iterator support for removal of elements.
Old Timer Favorite (it still works):
List<String> list;
for(int i = list.size() - 1; i >= 0; --i)
{
if(list.get(i).contains("bad"))
{
list.remove(i);
}
}
Benefits:
- It only iterates over the list once
- No extra objects created, or other unneeded complexity
- No problems with trying to use the index of a removed item, because... well, think about it!
Videos
Hi few days ago i was encountered a problem. While iterating an array list by a for each loop, i was trying to remove the current object in spesific conditional. Like:
Arraylist<someclass> mylist;
...
for (someclass element: mylist)
if element.data!=0; mylist.remove(element)
when i run the code it throws ConcurrentModificationException .
I think it is because of the size of arraylist changes and the next iteration also changes and it throws out error.
Then I remembered the solition from a book.
By iterating with a classic for loop from backwards, adding or removing an object to/from the end of the list wont effect the loop. So it should be:
for (int i=mylist.size()-1;i>=0;i--)
someclass element=mylist.get(i);
if element.data!=0; mylist.remove(i)
with this method it works and there was no error.
I was talking with u/AgardenerCoding about the problem. And he encouraged me to post this as it may be very useful for someone else searching for an answer to same problem.
In your case, there's no need to iterate through the list, because you know which object to delete. You have several options. First you can remove the object by index (so if you know, that the object is the second list element):
a.remove(1); // indexes are zero-based
Or, you can remove the first occurence of your string:
a.remove("acbd"); // removes the first String object that is equal to the
// String represented by this literal
Or, remove all strings with a certain value:
while(a.remove("acbd")) {}
It's a bit more complicated, if you have more complex objects in your collection and want to remove instances, that have a certain property. So that you can't remove them by using remove with an object that is equal to the one you want to delete.
In those case, I usually use a second list to collect all instances that I want to delete and remove them in a second pass:
List<MyBean> deleteCandidates = new ArrayList<>();
List<MyBean> myBeans = getThemFromSomewhere();
// Pass 1 - collect delete candidates
for (MyBean myBean : myBeans) {
if (shallBeDeleted(myBean)) {
deleteCandidates.add(myBean);
}
}
// Pass 2 - delete
for (MyBean deleteCandidate : deleteCandidates) {
myBeans.remove(deleteCandidate);
}
One-liner (java8):
list.removeIf(s -> s.equals("acbd")); // removes all instances, not just the 1st one
(does all the iterating implicitly)
There are several ways to do this. Let's look at the alternatives:
Iterating over a copy, removing from original
This is a simple solution for the underlying problem of your first code: A ConcurrentModificationException is thrown because you iterate through the list and removing from it at the same time.
Easy solution is to create a copy of the list and iterate through that.
for (Integer integer : new ArrayList<>(nums)) {
if (integer < 3) {
nums.remove(integer);
}
}
Down-sides of this approach:
- Creates a copy of the original list, which requires memory and an operation which performance depends on the type of the list (ArrayList, LinkedList, etc.)
- Additionally,
nums.remove(value)is a \$O(n)\$ operation. Making this loop overall \$O(n^2)\$
Java 8 Streams
List<Integer> filteredList = nums.stream().filter(i -> i >= 3).collect(Collectors.toList());
Down-sides:
- Does not actually modify the existing list, so if references to the list are spread around various variables, you still have some old elements that just shouldn't be in that list.
- Creates various stream-related objects which might not be the most effective option.
On the up-side, this is among the fastest for bigger lists.
If you're not using Java 8:
List<Object> originalList;
List<Object> newList = new YourFavoriteListType<>();
for (Object obj : originalList) {
if (shouldKeep(obj)) {
newList.add(obj);
}
}
Java 8 method
nums.removeIf(i -> i < 3);
Java 8 introduced the default method removeIf on the Collection interface. This allows different implementations to have implementation-specific performance-optimized implementations of this method.
Iterator.remove()
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
Integer integer = it.next();
if (integer < 3) {
it.remove();
}
}
The only down-side of this approach is that you need to switch your for-each to a while. However, this approach is the most efficient one, especially for LinkedList where it is \$O(n)\$ (it's \$O(n^2)\$ for ArrayList because it has to copy array data on each remove(index) call). This is the approach I would recommend in most cases.
Note: Instead of using a while-loop it can also be written as:
for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
Integer integer = it.next();
...
Conclusion
If you want to mutate the existing list, removeIf is the solution I would go with. If you like functional programming and prefer a new list instead of mutating the existing one, then go with the list.stream().filter(...).collect(Collectors.toList()) approach.
See also
"When to use LinkedList over ArrayList?" on Stack Overflow
Just had to do something very similar (hence why I'm here), ended up using Java8's Collection.removeIf(Predicate<? super E> filter)
With your code it would look like:
nums.removeIf((Integer i)->{return i<3;});
And if you wanted to collect the removes:
List<Integer> removed = new ArrayList<>();
nums.removeIf(
(Integer i)->{
boolean remove = i<3;
if (remove) {
removed.add(i);
}
return remove;
});
ArrayList removes objects based on the equals(Object obj) method. So you should implement properly this method. Something like:
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (!(obj instanceof ArrayTest)) return false;
ArrayTest o = (ArrayTest) obj;
return o.i == this.i;
}
Or
public boolean equals(Object obj) {
if (obj instanceof ArrayTest) {
ArrayTest o = (ArrayTest) obj;
return o.i == this.i;
}
return false;
}
If you are using Java 8 or above:
test.removeIf(t -> t.i == 1);
Java 8 has a removeIf method in the collection interface. For the ArrayList, it has an advanced implementation (order of n).