15-100 Lectures 25-26

Combined Notes

We began a Linked List class on Wednesday and finished it on Thursday. In order to keep all of the code in one place, both sets of lecture notes are combined here.

The Notes

...are incorporated into the code as comments.

The Code

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 void addFirst(Object o) {
      head = new Node (o, head);
    
      if (tail == null) tail = head;
      count++;
    }
    
    
    public void addLast(Object o) {
    
      if (tail == null) {
        addFirst(o);
        // Count increased in addFirst() 
        return;
      }
    
      tail.setNext(new Node (o, null));
      tail = tail.getNext();
      count++;
    }
    
    
    public Object getFirst() {  
      if (head == null)
        return null;
        
        return head.getItem();
    }
    
    
    public Object getLast() {  
      if (tail == null)
        return null;
        
        return tail.getItem();
    }
    
    public Object getNth (int n) {
    
      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();
    }
    
    
  public int indexOf (Object o) {    
    int posn;
    Node index;
    
    for (index=head, posn=0;
      ((index!=null) && (!index.getItem().equals(o)));
        index=index.getNext(), posn++ )
    ;
    
    if (posn > (this.count-1))
      return -1;
    
    return posn;
  }
  
  public boolean insertAtN (Object o, int n) {
  
    if ( (n <0) || (count <  n))
      return false;
      
    if (n == 0) {
      addFirst(o);
      // addFirst() increments count
      return true;
    }
    
    Node indexNode;
    int indexInt;
    
    for (indexInt=0, indexNode = head; 
         indexInt < (n-1); 
         indexInt++, indexNode = indexNode.getNext())
    ;
    
    indexNode.setNext (new Node(o, indexNode.getNext()));
    count++;
    
    // Fix the case of adding after tail
    if (tail.getNext() != null)
      tail = tail.getNext();
    
    return true;  
  }
  

  public Object removeNth (int n) {
    Object saved;

    if ( (n < 0) || (n >= count))
      return null;

    if (n == 0) {
      saved = head.getItem();
      head = head.getNext();
      count--;
      return saved;
    }

    Node indexNode;
    int i;
    
    for (i = 0, indexNode = head; 
         (i < (n - 1)); 
         i++, indexNode = indexNode.getNext())
    ;
  
    saved = indexNode.getNext().getItem();
  
    count--;

  
    if (tail == indexNode.getNext()) 
      tail = indexNode;

    indexNode.setNext(indexNode.getNext().getNext());
  
    return saved;
  }  
}