There are many solutions, that can be appropriate depending on your needs.
Solution 1 : fixed-size view
The classical way to iterate on a subset of a list is to create a narrower view of the original list, and to iterate on this whole view. The subList
method is used to create such a view.
List<Integer> l = // initialization code
int limitIndex = Math.max(l.size()-1, 0); // needed for empty list
for (int v : l.subList(0, limitIndex)) {
// your code
}
Note that I used a 'foreach' loop, which is a convenient way to use iterators. This is strictly equivalent to this code :
Iterator<Integer> it = l.subList(0, limitIndex).iterator();
while(it.hasNext()) {
int v = it.next();
// your code
}
Also, note that the subList
method does not create a new list : it only creates a 'view' on the original list. The content of the original list is not copied, and all changes made to the original list are visible from the list created by subList
.
Solution 2 : a custom Iterator/Iterable
If all you need is an iterator that always iterates from 0 to n-1, you can define a new Iterable
tailored to that particular need.
public class NoLastIterable<T> implements Iterable<T> {
private final List<T> backend;
public NoLastIterable(List<T> backend) {
this.backend = backend;
}
public Iterator<T> iterator() {
return new Iterator<T>() {
private int nextIndex;
public boolean hasNext() {
return nextIndex < backend.size() -1;
}
public T next() {
return backend.get(nextIndex++);
}
public void remove() {
throw new UnsupportedOperationException("not implemented");
}
};
}
}
This class is used like this :
for (int v : new NoLastIterable<Integer>(l)) {
// your code
}
Solution 3
You can even create your custom view of a List
, just like subList
does, but with more flexibility.
public class NoLastList<T> extends AbstractList<T> {
private final List<T> backend;
public NoLastList(List<T> backend) {
this.backend = backend;
}
@Override
public T get(int index) {
if (index >= size()) {
throw new IndexOutOfBoundsException();
}
return backend.get(index);
}
@Override
public int size() {
return Math.max(0, backend.size()-1);
}
}
Same usage as the other solutions :
for (int v : new NoLastList<Integer>(l)) {
// your code
}
The benefits of this solution can be seen in the following situation :
- the original list is created and initialized
- the
NoLastList
instance is created (as a view of the original List)
- some elements are added to the original list
In this situation, iterating over NoLastList
will take into account the elements that were added lately. NoLastList
always represents a view of the elements from 0 to n-1, even if n (the size) changes.