15-111 Lectures 16 (Friday, February 20, 2009)

The Code

Today in class, we began implementing a linked list. The notes are incorporated into the code as comments.

class LinkedList {

  /*
   * The Node class is private because it has no use outside of the Linked List
   * We make it private to make sure that it doesn't leak out and that no one
   * else uses it giving us maintenance problems
   */   
  private class Node {
  
    private Object item;
    private Node next;
    
    public Node (Object item, Node next) {
      this.item = item;
      this.next = next;
    }
    
    
    public Node (Object item) {
      this.item = item;
      this.next = null;
    }
    
    
    public Object getItem() { return item; }
    public Node getNext() { return next; }
    
    public void setNext(Node next) { this.next = next; }

    /*
     * Notice there is no setItem(...). This is intentional. But, it is
     * a decision that we're making -- not something that 'has to be'.
     * We're doing this to make sure that any time items are moved, they
     * are moved by mvoing around nodes, not just plucking them and 
     * moving them around -- it makes it easier to read. 
     *
     * Notice that we *must* have setNext(). Without that, changing 
     * one item would require changing every node before it. The 
     * predecessor would need a new node, then its predecessor, and so on. 
     */
    
    public String toString() {

      if (this.next != null)     
        return "[" + this.getItem() + "," + this.next.getItem() + "]";
      else
        return "[" + this.getItem() + "null]";
    }
    
    public boolean equals (Object o) {
      Node n = (Node) o;
   
      if (!this.getItem().equals(n.getItem()))
        return false;
      
      if (this.getNext() != n.getNext())
        return false;
  
      return true;
    }
  }
  

    /*
     * tail, below, isn't required, but it makes adds at the end faster. 
     * It doesn't help with removes at the end, because it is tail's 
     * predecssor that would need to be changed -- and we have no way to
     * go back from tail to its predecessor. 
     *
     * Similarly, count is for convenience. It gives us a fast way of 
     * implementing getCount(), if we'd like.
     *
     * Head, of course, is required. 
     */
  
    private Node head;
    private Node tail; 
    private int count;
    
    
    public LinkedList() {
      head = tail = null;
      count = 0;
    }
    
  
    public Object getNth (int n) {

      if ( (n < 0) || (n >= count)) return null;
    
      Node index;
      int count;
      for (index=head, count=0; 
           ((index!=null)&&(count<n)); 
           index=index.getNext(), count++)
      ;
      
      if (index == null)
        return null;
    
      return index.getItem();
    }
    
    
}