CS-151 Labs > Lab 4. My LinkedList


Part 2. My ListIterator

Once MyLinkedList class is working, you will need to create a ListIterator which is returned by the listIterator() method. You can do this through the use of a helper class MyLinkedListIterator nested inside MyLinkedList class, in the same way as it was implemented in the warmup:

class MyLinkedListIterator implements ListIterator<T> {

    // Instance variables here

    public MyLinkedListIterator() {
        // constructor code here
    }
    public boolean hasNext() {
        // Your code here
    }

    // other methods
}

The MyListIterator returns an object that enables list traversals by moving one node at a time in either direction. It might be helpful to think that the iterator has size+1 possible states: just before the head, between the 0 and 1 index, …, just after the tail. After one call to next(), the iterator is logically in the state shown below:

List iterator after a call to next()

You will need to implement all of the following methods. See interface ListIterator for details.

Public methods for MyListIterator

public boolean hasNext()
Returns true if there are more elements when going in the forward direction.
public T next()
Returns the next element in the list when going forward. Throw a NoSuchElementException if there is no next element.
public boolean hasPrevious()
Returns true if there are more elements when going in the reverse direction.
public T previous()
Returns the next element in the list when going backwards. Throw a NoSuchElementException if there is no previous element.

Programming Hints

Since the cursor of the ListIterator can be logically placed between two nodes, it may be useful to keep references to both the current Node and the next Node. You can also keep an int value of the index of the next node.

If you construct your MyLinkedList to use sentinel nodes as discussed in chapter 5.17, you shouldn’t have to worry about checking for null values at the ends of the list since the special head and tail nodes are always there.

You will need to keep some state information for the Iterator as well to check if the underlying list has not been modified since the iterator was created. Modifications can leave the iterator in an undefined state. This happens, for example, when the nodes around the current index are deleted. Therefore you should check that the list hasn’t been modified before you let an iterator move to the next state.

An easy way to do this is to have both the list and the iterator keep track of the number of modifications that have been made to the list. When the iterator is created it grabs the list’s modification count. If the iterator finds that its modification count is different from the list’s, you will know that the list structure has been changed and that the iterator is no longer viable (it refers to a list which has been modified). See modCount for additional details and suggestions.
Throw a ConcurrentModificationException in these cases.

Public Methods to add to MyLinkedList

Once you are sure your iterator is working, you should override the following methods in MyLinkedList.

public ListIterator<T> listIterator()
public Iterator<T> iterator()
These methods return a new instance of MyListIterator for the current list.

Testing your Iterator

You’ll want to test MyListIterator and be sure that it works properly. You should do this by printing out the entire list forwards and backwards using your iterator. Start at the beginning of the list and call hasNext() and next() using a loop until hasNext() returns false, printing out the entry at each step. This is similar to how you computed the sum in the warmup. Then, use hasPrevious() and previous() to return each entry until the iterator reaches the beginning of the list.

Test that if the underlying list is modified in the middle of the iteration (between two calls to next()), your iterator throws a ConcurrentModificationException.