In Java 8, we can use the Collection#removeIf API to remove items from a List while iterating it.
It is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances.
Using ArrayList's remove() method with normal for loop ArrayList's remove method can be used with normal for loop to remove an ArrayList in Java. If index is passed then it may give undesired result because of the fact that all the other elements are shifted to the left when an element is removed.
Replace element in arraylist while iterating Do not use iterator if you plan to modify the arraylist during iteration. Use standard for loop, and keep track of index position to check the current element. Then use this index to set the new element. Java program to search and replace an element in an ArrayList.
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
removeAl
technique works with any Collection (Collection, List, Set, etc). ListIterator
technique obviously only works with lists, provided that their given ListIterator
implementation offers support for add and remove operations. Iterator
approach would work with any type of collection, but it only supports remove operations.ListIterator
/Iterator
approach the obvious advantage is not having to copy anything since we remove as we iterate. So, this is very efficient. removeAll
approach 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. Iterator
interface is marked as "optional" in Javadocs, which means that there could be Iterator
implementations that throw UnsupportedOperationException
if 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.In Java 8, there is another approach. Collection#removeIf
eg:
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.removeIf(i -> i > 2);
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:
Are there any reasons to prefer one approach over the other
The first approach will work, but has the obvious overhead of copying the list.
The second approach will not work because many containers don't permit modification during iteration. This includes ArrayList
.
If the only modification is to remove the current element, you can make the second approach work by using itr.remove()
(that is, use the iterator's remove()
method, not the container's). This would be my preferred method for iterators that support remove()
.
Only second approach will work. You can modify collection during iteration using iterator.remove()
only. All other attempts will cause ConcurrentModificationException
.
You can't do the second, because even if you use the remove()
method on Iterator, you'll get an Exception thrown.
Personally, I would prefer the first for all Collection
instances, despite the additional overheard of creating the new Collection
, I find it less prone to error during edit by other developers. On some Collection implementations, the Iterator remove()
is supported, on other it isn't. You can read more in the docs for Iterator.
The third alternative, is to create a new Collection
, iterate over the original, and add all the members of the first Collection
to the second Collection
that are not up for deletion. Depending on the size of the Collection
and the number of deletes, this could significantly save on memory, when compared to the first approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With