ListIterator methods
ListIterator is implemented only by the classes that implement the
List interface (
ArrayList,
LinkedList, and
Vector). ListIterator provides the following.
| Result | Method | Description |
|---|
b = | it.hasNext() | true if there is a next element in the collection. |
obj = | it.next() | Returns the next object. |
b = | it.hasPrevious() | true if there is a previous element. |
obj = | it.previous() | Returns the previous element. |
i = | it.nextIndex() | Returns index of element that would be returned by subsequent call to next(). |
i = | it.previousIndex() | Returns index of element that would be returned by subsequent call to previous(). |
| it.add(obj) | Inserts obj in collection before the next element to be returned by next() and after an element that would be returned by previous(). |
| it.set() | Replaces the most recent element that was returned by next or previous(). |
| it.remove() | Removes the most recent element that was returned by next() or previous(). |
So its of form:public interface ListIterator extends Iterator {
boolean hasNext();
Object next();
boolean hasPrevious();
Object previous();
int nextIndex();
int previousIndex();
void remove(); // Optional
void set(Object o); // Optional
void add(Object o); // Optional
}The three methods that
ListIterator inherits from
Iterator (
hasNext,
next, and
remove) are intended to do exactly the same thing in both interfaces. The
hasPrevious and
previous operations are exact analogues of
hasNext and
next. The former operations refer to the element before the (implicit) cursor, whereas the latter refer to the element after the cursor. The
previous operation moves the cursor backwards whereas
next moves it forwards.
Here's the standard idiom for iterating backwards through a list:
for (ListIterator i=l.listIterator(l.size()); i.hasPrevious(); ) {
Foo f = (Foo) i.previous();
...
}
Note the argument to
listIterator in the above idiom. The
List interface has two forms of the
listIterator method. The form with no arguments returns a
ListIterator positioned at the beginning of the list, and the form with an
int argument returns a
ListIterator positioned at the specified index. The index refers to the element that would be returned by an initial call to
next. If the index value of
n is used, then the initial call to
next would return null, since it would point just past the end of the list. An initial call to
previous would return the element whose index was
index-1.
In a list of length n, there are n+1 valid values for index, from 0 to n, inclusive. Intuitively speaking, the cursor is always between two elements, the one that would be returned by a call to
previous and the one that would be returned by a call to
next. The
n+1 valid
index values correspond to the
n+1 "gaps" between elements, from the gap before the first element to the gap after the last one. The diagram below shows the five possible cursor positions in a list containing four elements.
Element(0) Element(1) Element(2) Element(3)
^ ^ ^ ^ ^
Index: 0 1 2 3 4
Calls to
next and
previous can be intermixed, but you have to be a bit careful. The first call to
previous after a sequence of calls to
next returns the same element as the last call to
next. Similarly, the first call to
next after a sequence of calls to
previous returns the same element as the last call to
previous. This will become obvious if you stare at the foregoing text long enough. If it doesn't, go get yourself a steaming hot mug of Java, and try again.
nextIndex and previousIndex It should come as no surprise that the
nextIndex method returns the index of the element that would be returned by a subsequent call to
next, and
previousIndex returns the index of the element that would be returned by a subsequent call to
previous. These calls are typically used for one of two purposes: To report the position where something was found, or to record the position of the
ListIterator so that another
ListIterator with identical position can be created.
It should also come as no surprise that the number returned by
nextIndex is always one greater than the number returned by
previousIndex. This implies the behavior of the two boundary cases: a call to
previousIndex when the cursor is before the initial element returns
-1, and a call to
nextIndex when the cursor is after the final element returns
list.size()+1. To make all of this concrete, here's a possible implementation of
List.indexOf:
public int indexOf(Object o) {
for (ListIterator i = listIterator(); i.hasNext(); )
if (o==null ? i.next()==null : o.equals(i.next()))
return i.previousIndex();
return -1; // Object not found
}
Note that the
indexOf method returns
i.previousIndex() though it is traversing the list in the forward direction. This is because
i.nextIndex() would return the index of the element that we are about to examine, and we want to return the index of the element that we just examined. The
Iterator interface provides the
remove operation to remove from the
Collection the last element returned by
next. The
ListIterator interface provides two additional operations to modify the list:
set and
add. The
set method "overwrites" the last element returned by
next or
previous with the specified element. It is demonstrated by the following polymorphic algorithm to replace all occurrences of one specified value with another:
public static void replace(List l, Object val, Object newVal) {
for (ListIterator i = l.listIterator(); i.hasNext(); )
if (val==null ? i.next()==null : val.equals(i.next()))
i.set(newVal);
}
The only bit of trickiness in this example is the equality test between
val and
i.next. We have to special-case an
val value of
null in order to prevent a
NullPointerException. The
add method inserts a new element into the list, immediately
before the current cursor position. This method is illustrated in the following polymorphic algorithm to replace all occurrences of a specified value with the sequence of values contained in the specified list:
public static void replace(List l, Object val, List newVals) {
for (ListIterator i = l.listIterator(); i.hasNext(); ) {
if (val==null ? i.next()==null : val.equals(i.next())) {
i.remove();
for (Iterator j = newVals.iterator(); j.hasNext(); )
i.add(j.next());
}
}
}
BAD BAD BAD
- Q: What does this loop do? Note mixing of iterator with index.
ArrayList<String> alist = new ArrayList<String>();
// . . . Add Strings to alist
int i = 0;
for (Iterator<String> it = alist.iterator(); it.hasNext(); ) {
System.out.println(alist.get(i++));
}
- A: It throws an exception when it goes beyond the end.
- After
hasNext() returns true, the only way to advance the iterator is to call next(). But the element is retrived with get(), so the iterator is never advanced. hasNext() will continue to always be true (ie, there is a first element), and eventually the get() will request something beyond the end of the ArrayList. Use either the iterator scheme. for (Iterator<String> it = alist.iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
Or the indexing scheme, but don't mix them. for (int i=0; i < alist.size(); i++) {
System.out.println(alist.get(i));
}