Is it possible to add elements to a collection while iterating over it?
More specifically, I would like to iterate over a collection, and if an element satisfies a certain condition I want to add some other elements to the collection, and make sure that these added elements are iterated over as well. (I realise that this could lead to an unterminating loop, but I'm pretty sure it won't in my case.)
The Java Tutorial from Sun suggests this is not possible: "Note that Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress."
So if I can't do what I want to do using iterators, what do you suggest I do?
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.
There are two issues here:
The first issue is, adding to an Collection after an Iterator is returned. As mentioned, there is no defined behavior when the underlying Collection is modified, as noted in the documentation for Iterator.remove:
... The behavior of an iterator is
unspecified if the underlying
collection is modified while the
iteration is in progress in any way
other than by calling this method.
The second issue is, even if an Iterator could be obtained, and then return to the same element the Iterator was at, there is no guarantee about the order of the iteratation, as noted in the Collection.iterator method documentation:
... There are no guarantees concerning the
order in which the elements are
returned (unless this collection is an
instance of some class that provides a
guarantee).
For example, let's say we have the list [1, 2, 3, 4].
Let's say 5 was added when the Iterator was at 3, and somehow, we get an Iterator that can resume the iteration from 4. However, there is no guarentee that 5 will come after 4. The iteration order may be [5, 1, 2, 3, 4] -- then the iterator will still miss the element 5.
As there is no guarantee to the behavior, one cannot assume that things will happen in a certain way.
One alternative could be to have a separate Collection to which the newly created elements can be added to, and then iterating over those elements:
Collection<String> list = Arrays.asList(new String[]{"Hello", "World!"});
Collection<String> additionalList = new ArrayList<String>();
for (String s : list) {
// Found a need to add a new element to iterate over,
// so add it to another list that will be iterated later:
additionalList.add(s);
}
for (String s : additionalList) {
// Iterate over the elements that needs to be iterated over:
System.out.println(s);
}
Edit
Elaborating on Avi's answer, it is possible to queue up the elements that we want to iterate over into a queue, and remove the elements while the queue has elements. This will allow the "iteration" over the new elements in addition to the original elements.
Let's look at how it would work.
Conceptually, if we have the following elements in the queue:
[1, 2, 3, 4]
And, when we remove 1, we decide to add 42, the queue will be as the following:
[2, 3, 4, 42]
As the queue is a FIFO (first-in, first-out) data structure, this ordering is typical. (As noted in the documentation for the Queue interface, this is not a necessity of a Queue. Take the case of PriorityQueue which orders the elements by their natural ordering, so that's not FIFO.)
The following is an example using a LinkedList (which is a Queue) in order to go through all the elements along with additional elements added during the dequeing. Similar to the example above, the element 42 is added when the element 2 is removed:
Queue<Integer> queue = new LinkedList<Integer>();
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);
while (!queue.isEmpty()) {
Integer i = queue.remove();
if (i == 2)
queue.add(42);
System.out.println(i);
}
The result is the following:
1
2
3
4
42
As hoped, the element 42 which was added when we hit 2 appeared.
You may also want to look at some of the more specialised types, like ListIterator, NavigableSet and (if you're interested in maps) NavigableMap.
Actually it is rather easy. Just think for the optimal way.
I beleive the optimal way is:
for (int i=0; i<list.size(); i++) {
Level obj = list.get(i);
//Here execute yr code that may add / or may not add new element(s)
//...
i=list.indexOf(obj);
}
The following example works perfectly in the most logical case - when you dont need to iterate the added new elements before the iteration element. About the added elements after the iteration element - there you might want not to iterate them either. In this case you should simply add/or extend yr object with a flag that will mark them not to iterate them.
Use ListIterator as follows:
List<String> l = new ArrayList<>();
l.add("Foo");
ListIterator<String> iter = l.listIterator(l.size());
while(iter.hasPrevious()){
String prev=iter.previous();
if(true /*You condition here*/){
iter.add("Bah");
iter.add("Etc");
}
}
The key is to iterate in reverse order - then the added elements appear on the next iteration.
I know its been quite old. But thought of its of any use to anyone else. Recently I came across this similar problem where I need a queue that is modifiable during iteration. I used listIterator to implement the same much in the same lines as of what Avi suggested -> Avi's Answer. See if this would suit for your need.
ModifyWhileIterateQueue.java
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ModifyWhileIterateQueue<T> {
ListIterator<T> listIterator;
int frontIndex;
List<T> list;
public ModifyWhileIterateQueue() {
frontIndex = 0;
list = new ArrayList<T>();
listIterator = list.listIterator();
}
public boolean hasUnservicedItems () {
return frontIndex < list.size();
}
public T deQueue() {
if (frontIndex >= list.size()) {
return null;
}
return list.get(frontIndex++);
}
public void enQueue(T t) {
listIterator.add(t);
}
public List<T> getUnservicedItems() {
return list.subList(frontIndex, list.size());
}
public List<T> getAllItems() {
return list;
}
}
ModifyWhileIterateQueueTest.java
#Test
public final void testModifyWhileIterate() {
ModifyWhileIterateQueue<String> queue = new ModifyWhileIterateQueue<String>();
queue.enQueue("one");
queue.enQueue("two");
queue.enQueue("three");
for (int i=0; i< queue.getAllItems().size(); i++) {
if (i==1) {
queue.enQueue("four");
}
}
assertEquals(true, queue.hasUnservicedItems());
assertEquals ("[one, two, three, four]", ""+ queue.getUnservicedItems());
assertEquals ("[one, two, three, four]", ""+queue.getAllItems());
assertEquals("one", queue.deQueue());
}
Using iterators...no, I don't think so. You'll have to hack together something like this:
Collection< String > collection = new ArrayList< String >( Arrays.asList( "foo", "bar", "baz" ) );
int i = 0;
while ( i < collection.size() ) {
String curItem = collection.toArray( new String[ collection.size() ] )[ i ];
if ( curItem.equals( "foo" ) ) {
collection.add( "added-item-1" );
}
if ( curItem.equals( "added-item-1" ) ) {
collection.add( "added-item-2" );
}
i++;
}
System.out.println( collection );
Which yeilds:
[foo, bar, baz, added-item-1, added-item-2]
Besides the solution of using an additional list and calling addAll to insert the new items after the iteration (as e.g. the solution by user Nat), you can also use concurrent collections like the CopyOnWriteArrayList.
The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created. This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException.
With this special collection (usually used for concurrent access) it is possible to manipulate the underlying list while iterating over it. However, the iterator will not reflect the changes.
Is this better than the other solution? Probably not, I don't know the overhead introduced by the Copy-On-Write approach.
public static void main(String[] args)
{
// This array list simulates source of your candidates for processing
ArrayList<String> source = new ArrayList<String>();
// This is the list where you actually keep all unprocessed candidates
LinkedList<String> list = new LinkedList<String>();
// Here we add few elements into our simulated source of candidates
// just to have something to work with
source.add("first element");
source.add("second element");
source.add("third element");
source.add("fourth element");
source.add("The Fifth Element"); // aka Milla Jovovich
// Add first candidate for processing into our main list
list.addLast(source.get(0));
// This is just here so we don't have to have helper index variable
// to go through source elements
source.remove(0);
// We will do this until there are no more candidates for processing
while(!list.isEmpty())
{
// This is how we get next element for processing from our list
// of candidates. Here our candidate is String, in your case it
// will be whatever you work with.
String element = list.pollFirst();
// This is where we process the element, just print it out in this case
System.out.println(element);
// This is simulation of process of adding new candidates for processing
// into our list during this iteration.
if(source.size() > 0) // When simulated source of candidates dries out, we stop
{
// Here you will somehow get your new candidate for processing
// In this case we just get it from our simulation source of candidates.
String newCandidate = source.get(0);
// This is the way to add new elements to your list of candidates for processing
list.addLast(newCandidate);
// In this example we add one candidate per while loop iteration and
// zero candidates when source list dries out. In real life you may happen
// to add more than one candidate here:
// list.addLast(newCandidate2);
// list.addLast(newCandidate3);
// etc.
// This is here so we don't have to use helper index variable for iteration
// through source.
source.remove(0);
}
}
}
For examle we have two lists:
public static void main(String[] args) {
ArrayList a = new ArrayList(Arrays.asList(new String[]{"a1", "a2", "a3","a4", "a5"}));
ArrayList b = new ArrayList(Arrays.asList(new String[]{"b1", "b2", "b3","b4", "b5"}));
merge(a, b);
a.stream().map( x -> x + " ").forEach(System.out::print);
}
public static void merge(List a, List b){
for (Iterator itb = b.iterator(); itb.hasNext(); ){
for (ListIterator it = a.listIterator() ; it.hasNext() ; ){
it.next();
it.add(itb.next());
}
}
}
a1 b1 a2 b2 a3 b3 a4 b4 a5 b5
I prefer to process collections functionally rather than mutate them in place. That avoids this kind of problem altogether, as well as aliasing issues and other tricky sources of bugs.
So, I would implement it like:
List<Thing> expand(List<Thing> inputs) {
List<Thing> expanded = new ArrayList<Thing>();
for (Thing thing : inputs) {
expanded.add(thing);
if (needsSomeMoreThings(thing)) {
addMoreThingsTo(expanded);
}
}
return expanded;
}
IMHO the safer way would be to create a new collection, to iterate over your given collection, adding each element in the new collection, and adding extra elements as needed in the new collection as well, finally returning the new collection.
Given a list List<Object> which you want to iterate over, the easy-peasy way is:
while (!list.isEmpty()){
Object obj = list.get(0);
// do whatever you need to
// possibly list.add(new Object obj1);
list.remove(0);
}
So, you iterate through a list, always taking the first element and then removing it. This way you can append new elements to the list while iterating.
Forget about iterators, they don't work for adding, only for removing. My answer applies to lists only, so don't punish me for not solving the problem for collections. Stick to the basics:
List<ZeObj> myList = new ArrayList<ZeObj>();
// populate the list with whatever
........
int noItems = myList.size();
for (int i = 0; i < noItems; i++) {
ZeObj currItem = myList.get(i);
// when you want to add, simply add the new item at last and
// increment the stop condition
if (currItem.asksForMore()) {
myList.add(new ZeObj());
noItems++;
}
}
I tired ListIterator but it didn't help my case, where you have to use the list while adding to it. Here's what works for me:
Use LinkedList.
LinkedList<String> l = new LinkedList<String>();
l.addLast("A");
while(!l.isEmpty()){
String str = l.removeFirst();
if(/* Condition for adding new element*/)
l.addLast("<New Element>");
else
System.out.println(str);
}
This could give an exception or run into infinite loops. However, as you have mentioned
I'm pretty sure it won't in my case
checking corner cases in such code is your responsibility.
This is what I usually do, with collections like sets:
Set<T> adds = new HashSet<T>, dels = new HashSet<T>;
for ( T e: target )
if ( <has to be removed> ) dels.add ( e );
else if ( <has to be added> ) adds.add ( <new element> )
target.removeAll ( dels );
target.addAll ( adds );
This creates some extra-memory (the pointers for intermediate sets, but no duplicated elements happen) and extra-steps (iterating again over changes), however usually that's not a big deal and it might be better than working with an initial collection copy.
Even though we cannot add items to the same list during iteration, we can use Java 8's flatMap, to add new elements to a stream. This can be done on a condition. After this the added item can be processed.
Here is a Java example which shows how to add to the ongoing stream an object depending on a condition which is then processed with a condition:
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList = intList.stream().flatMap(i -> {
if (i == 2) return Stream.of(i, i * 10); // condition for adding the extra items
return Stream.of(i);
}).map(i -> i + 1)
.collect(Collectors.toList());
System.out.println(intList);
The output of the toy example is:
[2, 3, 21, 4]
In general, it's not safe, though for some collections it may be. The obvious alternative is to use some kind of for loop. But you didn't say what collection you're using, so that may or may not be possible.
In Java is there an easy/convenient way to convert a collection of N items into N collections each with one item? I mean other than manually going through each item in the collection and copy it into a new collection.
Here's a way to do it with one statement in Java 8. l1 is some Collection<T>, and the result is a List<ArrayList<T>> where each ArrayList<T> contains one element from the collection.
List<ArrayList<T>> l2 = l1.stream().map(x -> new ArrayList<>(Arrays.asList(x))).collect(Collectors.toList());
Broken down, this:
Creates a Stream of all elements from the collection;
Maps each element to an ArrayList that contains that element;
Collects the resulting ArrayLists into a List<ArrayList>.
If you are willing to use guava, you could have a Function < E, List< E >> that looks like:
public final class ItemToListFunction<E> implements Function<E, List<E>>
{
public List<E> apply(E input)
{
//if it does not need to be mutable
return Collections.singletonList(input);
//if it does need to be mutable
final List<E> list = new ArrayList<E>();
list.add(input);
return list;
}
}
You can then use one of the transform methods in Collections2, Lists, or Iterables.
The advantage of this approach is that the creation of the internal lists is deferred until actual iteration over the elements.
Is there a Guava Iterator (or methodology) for List objects which allows two iterator instances to exist - in the same memory scope - while allowing the remove() operation? (Bonus point: if it works for a Collection).
Example use case: an outer and an inner iteration through a collection, where the inner loop might decide to remove an element and the outer loop would subsequently skip it.
Imagine how it would benefit the following concept code (which uses Guava static imports) by reducing the number of elements to compare in the loop and also removing the need to remove empty sets from the list at the end:
private <T> Set<Set<T>> disjointify(Collection<Set<T>> sets) {
List<Set<T>> disjoint = newArrayList(sets);
for (Set<T> set1 : disjoint) {
for (Set<T> set2 : filter(disjoint, not(equalTo(set1)))) {
if (!intersection(set1, set2).isEmpty()) {
// this wouldn't be safe for a Set<Set<T>>
set1.addAll(set2);
set2.clear();
}
}
}
return newHashSet(filter(disjoint, NO_EMPTIES));
}
private static final Predicate<Set<?>> NO_EMPTIES = new Predicate<Set<?>>() {
#Override
public boolean apply(Set<?> input) {
if (input == null || input.isEmpty()) {
return false;
}
return true;
}
};
Note: one can easily imagine creating the implementation - especially for LinkedList - I'm just asking if one already exists here.
For the record, if an efficient Iterable did already exist, and worked for Sets, then the use case would look like the following (I have created my own very inefficient Iterable which achieves this, but it is 50 lines long and ludicrously inefficient - so I use the original code above):
private <T> void disjointify(Set<Set<T>> sets) {
for (Set<T> set1 : nestable(sets)) {
Iterator<Set<T>> it = filter(nestable(sets), not(equalTo(set1))).iterator();
while (it.hasNext()) {
Set<T> set2 = it.next();
if (!intersection(set1, set2).isEmpty()) {
set1.addAll(set2);
it.remove();
}
}
}
}
It would appear that such an implementation does not exist in standard libraries.
Why not just filter the outer look with NO_EMPTIES? Since filter is evaluated during iteration, any newly empty sets would not be returned in the filtered list / outer loop.
Otherwise, I don't think so. You would get a ConcurrentModificationException on the outter loop.
lets say I have an List. There is no problem to modify list's item in for loop:
for (int i = 0; i < list.size(); i++) { list.get(i).setId(i); }
But I have a SortedSet instead of list. How can I do the same with it?
Thank you
First of all, Set assumes that its elements are immutable (actually, mutable elements are permitted, but they must adhere to a very specific contract, which I doubt your class does).
This means that generally you can't modify a set element in-place like you're doing with the list.
The two basic operations that a Set supports are the addition and removal of elements. A modification can be thought of as a removal of the old element followed by the addition of the new one:
You can take care of the removals while you're iterating, by using Iterator.remove();
You could accumulate the additions in a separate container and call Set.addAll() at the end.
You cannot modify set's key, because it causes the set rehasing/reordering. So, it will be undefined behaviour how the iteration will run further.
You could remove elements using iterator.remove(). But you cannot add elements, usually better solution is to accumulate them in a new collection and addAll it after the iteration.
Set mySet = ...;
ArrayList newElems = new ArrayList();
for(final Iterator it = mySet.iterator(); it.hasNext(); )
{
Object elem = it.next();
if(...)
newElems.add(...);
else if(...)
it.remove();
...
}
mySet.addAll(newElems);
Since Java 1.6, you're able to use a NavigableSet.
You should use an Iterator or better still the enhanced for-loop syntax (which depends on the class implementing the Iterable interface), irrespective of the Collection you're using. This abstracts away the mechanism used to traverse the collection and allows a new implementation to be substituted in without affecting the iteration routine.
For example:
Set<Foo> set = ...
// Enhanced for-loop syntax
for (Foo foo : set) {
// ...
}
// Iterator approach
Iterator it = set.iterator();
while (it.hasNext()) {
Foo foo = it.next();
}
EDIT
Kan makes a good point regarding modifying the item's key. Assuming that your class's equals() and hashCode() methods are based solely on the "id" attribute (which you're changing) the safest approach would be to explicitly remove these from the Set as you iterate and add them to an "output" Set; e.g.
SortedSet<Foo> input = ...
SortedSet<Foo> output = new TreeSet<Foo>();
Iterator<Foo> it = input.iterator();
while (it.hasNext()) {
Foo foo = it.next();
it.remove(); // Remove from input set before updating ID.
foo.setId(1);
output.add(foo); // Add to output set.
}
You cannot do that. But you may try, maybe you'll succeed, maybe you'll get ConcurrentModificationException. It's very important to remember, that modifying elements while iterating may have unexpected results. You should instead collect that elements in some collection. And after the iteration modify them one by one.
This will only work, if id is not used for equals, or the comperator you used for the sorted set:
int counter = 0;
for(ElementFoo e : set) {
e.setId(counter);
couter++;
}
What is the best way to get value from java.util.Collection by index?
You shouldn't. a Collection avoids talking about indexes specifically because it might not make sense for the specific collection. For example, a List implies some form of ordering, but a Set does not.
Collection<String> myCollection = new HashSet<String>();
myCollection.add("Hello");
myCollection.add("World");
for (String elem : myCollection) {
System.out.println("elem = " + elem);
}
System.out.println("myCollection.toArray()[0] = " + myCollection.toArray()[0]);
gives me:
elem = World
elem = Hello
myCollection.toArray()[0] = World
whilst:
myCollection = new ArrayList<String>();
myCollection.add("Hello");
myCollection.add("World");
for (String elem : myCollection) {
System.out.println("elem = " + elem);
}
System.out.println("myCollection.toArray()[0] = " + myCollection.toArray()[0]);
gives me:
elem = Hello
elem = World
myCollection.toArray()[0] = Hello
Why do you want to do this? Could you not just iterate over the collection?
I agree with Matthew Flaschen's answer and just wanted to show examples of the options for the case you cannot switch to List (because a library returns you a Collection):
List list = new ArrayList(theCollection);
list.get(5);
Or
Object[] list2 = theCollection.toArray();
doSomethingWith(list[2]);
If you know what generics is I can provide samples for that too.
Edit: It's another question what the intent and semantics of the original collection is.
In general, there is no good way, as Collections are not guaranteed to have fixed indices. Yes, you can iterate through them, which is how toArray (and other functions) work. But the iteration order isn't necessarily fixed, and if you're trying to index into a general Collection, you're probably doing something wrong. It would make more sense to index into a List.
I agree that this is generally a bad idea. However, Commons Collections had a nice routine for getting the value by index if you really need to:
CollectionUtils.get(collection, index)
You must either wrap your collection in a list (new ArrayList(c)) or use c.toArray() since Collections have no notion of "index" or "order".
Convert the collection into an array by using function
Object[] toArray(Object[] a)
It would be just as convenient to simply convert your collection into a list whenever it updates. But if you are initializing, this will suffice:
for(String i : collectionlist){
arraylist.add(i);
whateverIntID = arraylist.indexOf(i);
}
Be open-minded.
you definitively want a List:
The List interface provides four methods for positional (indexed) access to list elements.
Lists (like Java arrays) are zero based.
Also
Note that these operations may execute in time proportional to the index value for some
implementations (the LinkedList class, for example). Thus, iterating over the elements in a > list is typically preferable to indexing through it if the caller does not know the
implementation.
If you need the index in order to modify your collection you should note that List provides a special ListIterator that allow you to get the index:
List<String> names = Arrays.asList("Davide", "Francesco", "Angelocola");
ListIterator<String> i = names.listIterator();
while (i.hasNext()) {
System.out.format("[%d] %s\n", i.nextIndex(), i.next());
}
use for each loop...
ArrayList<Character> al = new ArrayList<>();
String input="hello";
for (int i = 0; i < input.length(); i++){
al.add(input.charAt(i));
}
for (Character ch : al) {
System.Out.println(ch);
}
If your Collection is a List, simply cast it as a List and call get(final int index). Otherwise, it might make sense to consider finding the nth element in an ordered set, for example if it's a LinkedHashSet respecting insertion order (keep in mind that it's possible to create such an instance not respecting insertion order), you can use Collection.stream().skip(index).limit(1).findFirst().orElse(null).
You can get the value from collection using for-each loop or using iterator interface. For a Collection c
for (<ElementType> elem: c)
System.out.println(elem);
or Using Iterator Interface
Iterator it = c.iterator();
while (it.hasNext())
System.out.println(it.next());