Design an iterator for a collection of collections in java. The iterator should hide the nesting, allowing you to iterate all of the elements belonging to all of the collect
In this post you can see two implementations, the only (minor) difference is that it takes an iterator of iterators instead of a collection of collections.
This difference combined with the requirement to iterate the elements in a round-robin fashion (a requirement that wasn't requested by the OP in this question) adds the overhead of copying the iterators into a list.
The first approach is lazy: it will iterate an element only when this element is requested, the 'price' we have to pay is that the code is more complex because it needs to handle more edge-cases:
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
public class MultiIterator implements Iterator {
List> iterators = new LinkedList<>();
Iterator current = null;
public MultiIterator(Iterator> iterator) {
// copy the iterators into a list
while (iterator.hasNext()) {
iterators.add(iterator.next());
}
}
@Override
public boolean hasNext() {
boolean result = false;
if (iterators.isEmpty() && (current == null || !current.hasNext())) {
return false;
}
if (current == null) {
current = iterators.remove(0);
}
while (!current.hasNext() && !iterators.isEmpty()) {
current = iterators.remove(0);
}
if (current.hasNext()) {
result = true;
}
return result;
}
@Override
public E next() {
if (current == null) {
try {
current = iterators.remove(0);
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
E result = current.next(); // if this method was called without checking 'hasNext' this line might raise NoSuchElementException which is fine
iterators.add(current);
current = iterators.remove(0);
return result;
}
// test
public static void main(String[] args) {
List a = new LinkedList<>();
a.add(1);
a.add(7);
a.add(13);
a.add(17);
List b = new LinkedList<>();
b.add(2);
b.add(8);
b.add(14);
b.add(18);
List c = new LinkedList<>();
c.add(3);
c.add(9);
List d = new LinkedList<>();
d.add(4);
d.add(10);
d.add(15);
List e = new LinkedList<>();
e.add(5);
e.add(11);
List f = new LinkedList<>();
f.add(6);
f.add(12);
f.add(16);
f.add(19);
List> iterators = new LinkedList<>();
iterators.add(a.iterator());
iterators.add(b.iterator());
iterators.add(c.iterator());
iterators.add(d.iterator());
iterators.add(e.iterator());
iterators.add(f.iterator());
MultiIterator it = new MultiIterator<>(iterators.iterator());
while (it.hasNext()) {
System.out.print(it.next() + ","); // prints: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
}
}
}
and the second ('greedy' copying of all the elements from all the iterators in the requested order into a list and returning an iterator to that list ):
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class MultiIterator {
Iterator> iterator = null;
List elements = new LinkedList<>();
private MultiIterator(Iterator> iterator) {
this.iterator = iterator;
}
private void copyElementsInOrder() {
List> iterators = new LinkedList<>();
// copy the iterators into a list
while (iterator.hasNext()) {
iterators.add(iterator.next());
}
// go over the list, round-robin, and grab one
// element from each sub-iterator and add it to *elements*
// empty sub-iterators will get dropped off the list
while (!iterators.isEmpty()) {
Iterator subIterator = iterators.remove(0);
if (subIterator.hasNext()) {
elements.add(subIterator.next());
iterators.add(subIterator);
}
}
}
public static Iterator iterator(Iterator> iterator) {
MultiIterator instance = new MultiIterator<>(iterator);
instance.copyElementsInOrder();
return instance.elements.iterator();
}
// test
public static void main(String[] args) {
List a = new LinkedList<>();
a.add(1);
a.add(7);
a.add(13);
a.add(17);
List b = new LinkedList<>();
b.add(2);
b.add(8);
b.add(14);
b.add(18);
List c = new LinkedList<>();
c.add(3);
c.add(9);
List d = new LinkedList<>();
d.add(4);
d.add(10);
d.add(15);
List e = new LinkedList<>();
e.add(5);
e.add(11);
List f = new LinkedList<>();
f.add(6);
f.add(12);
f.add(16);
f.add(19);
List> iterators = new LinkedList<>();
iterators.add(a.iterator());
iterators.add(b.iterator());
iterators.add(c.iterator());
iterators.add(d.iterator());
iterators.add(e.iterator());
iterators.add(f.iterator());
Iterator it = MultiIterator.iterator(iterators.iterator());
while (it.hasNext()) {
System.out.print(it.next() + ","); // prints: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
}
}
}
I included a simple 'test' code in order to show the way to use the MultiIterator, this is not always trivial (because of the use of Generics) as you can see on the line:
Iterator it = MultiIterator.iterator(iterators.iterator());