Return to the lecture notes index

Lecture #8 (September, 16 2005)

LinkedLists and Iterators

Today in lecture we will continue working with LinkedLists. You may have noticed in the java API that there is a list iterator. Iterators are used to walk through the data structure. Some iterators can only walk forward and some are only backward walking iterators, and some of the iterators are bidirectional iterators. With some of the iterators in java you have to change the data structure using the iterator. If you change the structure without using the iterator then the iterator is broken. In the Java API code it keeps a flag inside of the linkedlist. It sets the flag to false saying that the data structure has not been changed. If the data structure is changed it turns to true and throws an exception. This is called a quick fail model because if the iterator breaks it stops the program right away.

Today in class we are going to try to construct an iterator. This iterator will be similar in character to the ListIterator in java API

We are going to construct a simple iterator and it is going to have two simple methods. Iterator.java

interface Iterator{
   public boolean hadNext();
   public Object next();
}
 

I need to use the private elements of the linked list in order to make the iterator. If we did not have access then we would have to use count, and then count from zero each time we wanted to get a new node. But we want access inside the linkedlist class so we can maintain our node position.

So inside the LinkedList class LinkedList.java revised we make a

public class LinkedLIstIterator implements Iterator{

}

Implements means that the LinkedListIterator would have at least all the methods defined in the iterator class. Having the LinkedListIterator implement Iterator means two things, first of all it means LinkedListIterator has to have all the methods that are defined in Iterator. This also means that we will have an iterator variable inside the LinkedListIterator an can define LinkedListIterator as just an Iterator.

// this a rough attempt to write iterator we will revise it later

public class LinkedListIterator implements Iterator{

	private Node head; // since we are inside class LinkedList we can access the node
        	           // class which is private to LinkedLists
 // our constructor that sets LinkedListIterator's head equal to parameter head
  
  public LinkedListIterator(Node Head){
   this.head = head;
  }
// this method tells us that there an element in the list after this one

  public boolean hasNext(){
  	return head !=null;
  }

// this method is a one trick pony because it just walks through the list
// and then it is broken

   public Object next(){
	Object item = head.getItem();
	head = head.getNext();
	return item;
   }
}

Now we go down into the LinkedList class and write an iterator method

public Iterator iterator(){

  return new LinkedListIterator(head);

}

Why is it that we can return a LinkedListIterator when the return type is just Iterator? It is because the LinkedLIstIterator implements the interface for Iterator. If LinkedLIstIterator were to have other methods then those methods in Iterator then the extra methods would be hidden. The LinkedListIterator that was returned as a Iterator would not be able to run methods associated only with LinkedListIterator.

Questions we should try to answer:

Why was LinkedListIterator public?

LinkedListIterator is public So that it can be used by the programmer outside of the LinkedList class. For example the programmer may want to use it in the main method.

Why was LinkedListIterator nested with LinkedList?

LinkedListIterator was nested inside LinkedList so that we can use the private variables and methods in the linkedlist for example we used the head in the above LinkedListIterator class. If LinkedListIterator was not nested in LinkedList then we could not use it. Keep in mind that the node definition itself is also private so it had to be inside the LinkedList in order to use the node definition.

Question asked in class: Can you go over using the LinkedListIterator?

LinkedList ll = new LinkedList(0;
.....
Iterator li = new ll.iterator();

while(li.hasNext(){

Object o = li.next();
System.out.println(o);
}
// there you go a quick and dirty loop for printing a LinkedList

// or how about an insert method for the iterator like in the more powerful
//ListIterator already written in java API. The method is the ListIterator is 
// also called add instead of insert

The iterator pointer in this model points in between nodes allowing us to ask next even of the first(head) node. If the iterator pointer is equal to head I would want to insert the newNode before head.

public void insert(Object o); // in the Iterator interface

Next we have to write the insert method in the LinkedListClass

If we forget about the case of a head we are pointing at a node and we want to insert infront of that node. With the way LinkedLists are structured we would need to have a pointer always pointing to the node behind the node we are at. So we need to give the LinkedListIterator class another instance variable Node prev. This is initially set to null upon initialization because if the pointer is at head then there are is no previous node.

//now we can write an insert method for LinkedListIterator class
public void insert(Object o){

if(prev==null){

// adding first

head = new Node(item, head);

if(tail == null) tail=head;
{
// Now things just got complicated
}

This is a pretty broken way of constructing this so we then rewrote a lot of the code. Instead of head and tail, the LinkedListIterator will have a LinkedList instance variable. In addition instead of a prev Node will will have an Node index

public class LinkedListIterator implements Iterator{

  private LinkedList ll;
  private Node index;

 public LinkedLisetIterator (LinkedList ll){
   this.ll = ll
   index = ll.head;
 }

  public boolean hasNext(){
	return index != null;
  } 

 public Object next(){
   Object item = index.getItem();
   index = index.getNext();

   return item;
 }


 public void insert(Object o)
 {

    if( index==null){
	ll.addlast(o);
	return;	
    }
    if (index == ll.head){
        ll.addFirst(o);
	return;
    }
//first we took care of special cases

   // this wedges a new node inbetween the existing nodes
   index.setNext(new Node(o, index.getNext()) ); 
   index = index.getNext();
   ll.count++;

 }

}

Remember that this is quick and dirty code that we do in class and is not guarenteed to be 100% bug free. It is also not necesarily the way to code it on the labs. For labs we should have written a LinkedListIteratorException class and then handle the exceptions. This code is also not neccesarily the same as java API and therefore should not be used instead of it. These in class examples are learning tools and only meant to be learning tools.