Adding elements to a collection during iteration
How about building a Queue with the elements you want to iterate over; when you want to add elements, enqueue them at the end of the queue, and keep removing elements until the queue is empty. This is how a breadth-first search usually works.
Java - adding elements to list while iterating over it
You may use a ListIterator
which has support for a remove/add method during the iteration itself.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.add(new Book(...));
}
}
How to add element in List while iterating in java?
You can't use a foreach statement for that. The foreach is using internally an iterator:
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.
(From ArrayList javadoc)
In the foreach statement you don't have access to the iterator's add method and in any case that's still not the type of add that you want because it does not append at the end. You'll need to traverse the list manually:
int listSize = list.size();
for(int i = 0; i < listSize; ++i)
list.add("whatever");
Note that this is only efficient for Lists that allow random access. You can check for this feature by checking whether the list implements the RandomAccess marker interface. An ArrayList has random access. A linked list does not.
Add elements to a List while iterating over it. (Java)
You can't modify a Collection while iterating over it using an Iterator
, except for Iterator.remove()
.
However, if you use the listIterator()
method, which returns a ListIterator
, and iterate over that you have more options to modify. From the javadoc for add()
:
The new element is inserted before the implicit cursor: ... a subsequent call to
previous()
would return the new element
Given that, this code should work to set the new element as the next in the iteration:
ListIterator<T> i;
i.add(e);
i.previous(); // returns e
i.previous(); // returns element before e, and e will be next
This will work except when the list starts iteration empty, in which case there will be no previous element. If that's a problem, you'll have to maintain a flag of some sort to indicate this edge case.
Java collection to allow adding and removing while iterating
What you describe looks very similar to how CopyOnWriteArrayList
works:
- once you start iterating, you can change the collection (including from another thread) without affecting the iteration
- if you create a new iterator it will be based on the collection at the creation time
- it is thread safe
Simple example below with the following output:
Iterator 1 - 1
4 has been added
Iterator 2 - 1
Iterator 2 - 2
Iterator 2 - 3
Iterator 2 - 4
Iterator 1 - 2
Iterator 1 - 3
public static void main(String[] args) throws InterruptedException {
final List<Integer> list = new CopyOnWriteArrayList<Integer>();
list.addAll(Arrays.asList(1, 2, 3));
new Thread(new Runnable() {
@Override
public void run() {
for (Integer i : list) {
System.out.println("Iterator 1 - " + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
}
}).start();
Thread.sleep(10);
list.add(4);
System.out.println("4 has been added");
for (Integer i : list) {
System.out.println("Iterator 2 - " + i);
}
}
Concepts of modifying the element in collection while iterating?
Question1:- So can i say enhance for loop also uses the fail-fast iterator internally?Though when i execute below code it works fine
Yes, thats right. Have a look at the compiled code, with the javap
command to verify this if you like.
My guess is that modification means here removal or addition not for updation of element inside collection for list interface while it also includes modification of element for set interface . Right? Atleast the programmes i tried it is the case with them.
Thats right, if you do emp1.setEmpId(2)
or something similar, the iteration will not fail.
...it should throw the concurrent modification exception but it did not . Not sure why?
It only throws the exception if you modify the list. Keep in mind that the list contains references to objects. If you modify the objects, the references does not change, thus the list does not change.
How to modify a Collection while iterating using for-each loop without ConcurrentModificationException?
Use Iterator#remove
.
This is the only safe way to modify a collection during iteration. For more information, see The Collection Interface tutorial.
If you also need the ability to add elements while iterating, use a ListIterator
.
Remove elements from collection while iterating
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
removeAl
technique works with any Collection (Collection, List, Set, etc). - The
ListIterator
technique obviously only works with lists, provided that their givenListIterator
implementation offers support for add and remove operations. - The
Iterator
approach would work with any type of collection, but it only supports remove operations. - With the
ListIterator
/Iterator
approach 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
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. - I think it is worth mentioning that the remove method of the
Iterator
interface is marked as "optional" in Javadocs, which means that there could beIterator
implementations that throwUnsupportedOperationException
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.
How to add values to a list while iterating it
Looking at the ArrayList
API:
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.
So you're going to need to explicitly get a ListIterator
and use that to properly alter your ArrayList
. Also, I don't believe you can use a for-each loop because the iterators in those are separate from your explicitly retrieved iterator. I think you'll have to use a while loop:
List<String> xxx = new ArrayList<>();
ListIterator<String> iterator = xxx.listIterator();
while (iterator.hasNext()) {
String s = iterator.next();
for (String zzz : xyxy) {
if (!zzz.equals(s)) {
iterator.add(zzz); //<-- Adding is done through the iterator
}
}
}
Related Topics
Differencebetween Integer and Int in Java
How to Call a Static Method in Jsp/El
Url to Load Resources from the Classpath in Java
How to Sort List of Objects by Some Property
Strange Floating-Point Behaviour in a Java Program
Java Simpledateformat("Yyyy-Mm-Dd'T'Hh:Mm:Ss'Z'") Gives Timezone as Ist
What Is the Java's Internal Represention for String? Modified Utf-8? Utf-16
Seeking Useful Eclipse Java Code Templates
How to Implement a Map with Multiple Keys
Differencebetween Dynamic and Static Polymorphism in Java
Read Url to String in Few Lines of Java Code
How to Handle Iframe in Selenium Webdriver Using Java
How to Find Unused/Dead Code in Java Projects
How to Open the Default Webbrowser Using Java