Common Linked List Operations

Adding an Item/Node to the Front of a List

To add an item/node to the front of the list (i.e., the end referenced by head), one can use code similar to the following. Note, in combination with a method to remove the first item/node previously discussed, you have the basic elements for a linked list implementation of a stack.

public void addFirst(Item item) {
   Node n = new Node();
   n.item = item;
   n.next = head;
   head = n; 
}

Traversing a List

To do something to each element of a list (inspect it, modify it, etc.), we can traverse the list using something similar to the modified for-loop below.

for (Node n = head; n != null; n = n.next) {
   // process n.item
}

Adding an Item/Node to the End of a List

To add an item/node to the end of the list will require traversing the entire list (unless you also keep a reference to the last element as an instance variable). The trick is to remember to stop on the last node, and not on null, as it is this last node whose reference must be changed to accomplish the addition.

public void addLast(Item item) {
   Node newEnd = new Node();
   newEnd.item = item;
   newEnd.next = null;
   
   if (head == null) 
      head = newEnd;
   else {
      Node tmp = head;
      while (tmp.next != null) 
         tmp = tmp.next;
      tmp.next = newEnd; 
   }
}

Note, having to traverse the entire list is very inefficient -- just think of how much wasted time would be involved if your list was a million nodes long! Truly, the better way to do this is to add an instance variable (perhaps called tail) to the LinkedList class that keeps a reference to the last element of the list, as the below code demonstrates:

public void addLast(Item item) {
   Node newEnd = new Node();
   newEnd.item = item;
   newEnd.next = null;
   
   if (head == null)
      head = newEnd;
   else 
      tail.next = newEnd;
   
   tail = newEnd;    // don't forget to update tail!
}

Expanding the instance variables of any class should always be done cautiously, however! One must consider the cost and potential inefficiencies of having to update this new instance variable in all of the other methods the class employs.


Adding an Item/Node After a Key Item

To insert a node/item just after the first node in our list that contains some key item, we can traverse the list until the key is found and then adjust the references appropriately, as the below accomplishes. Note, if the key is not found -- for simplicity's sake -- the method below simply doesn't insert anything. One could let the client know this by returning a boolean to indicate whether or not the insertion happened. One could also throw an exception upon a failed insertion, if that was more appropriate.

public void insertAfter(Item key, Item itemToInsert) {
   Node nodeToInsert = new Node();
   nodeToInsert.item = itemToInsert;
   
   Node tmp = head;
   while ((tmp != null) && (! tmp.item.equals(key))) 
      tmp = tmp.next;
   
   if (tmp != null) {
      nodeToInsert.next = tmp.next;
      tmp.next = nodeToInsert;
   }
}

Adding an Item/Node Before a Key Item

Suppose one wished to insert a node/item just before the first node in our list that contains some key item. In this case we could, for the sake of convenience, maintain two references previous and current, so that as we traversed the list, looking for the key, we shifted these two references in tandem, always keeping previous one node before current. Then upon current reaching the node containing the key, we use both of these references to accomplish the insertion between them, as the code below indicates. (Note, in the code below -- as with the previous example -- if the key is never found, no insertion is made.)

public void insertBefore(Item key, Item itemToInsert) {
   Node nodeToInsert = new Node();
   nodeToInsert.item = itemToInsert;
   
   Node previous = null;
   Node current = head;
   
   while (current != null && (! current.item.equals(key))) {
      previous = current;
      current = current.next;
   }
   
   if (current != null) {
      if (current != head) {	
        previous.next = nodeToInsert;
        nodeToInsert.next = current;
      }
      else {
        head = nodeToInsert;
        head.next = current;
      }
   }
}

Deleting an Item/Node

To delete some key item (and its corresponding node) from the list, we again find it convenient to maintain two references previous and current. However, one should note that things must be handled differently when the key is found at the head of the list, as previous will still be null. (Here again, for simplicity's sake -- if the key is never found, nothing happens.)

public void removeFirstOccurrence(Item key)
{
   Node previous = null;
   Node current = head;
   
   while (current != null && (! current.item.equals(key))) {
      previous = current;
      current = current.next;
   }
   
   if (current != null) {
      if (current == head) 
         head = head.next;
      else 
         previous.next = current.next;
   }
}

Adding an Iterator

One can provide (sequential) access to the data stored in the list, while still hiding the underlying representation by adding an inner class that implements the Iterator interface and a iterator method, so that the LinkedList class can implement the Iterable interface, as the below code demonstrates.

public class LinkedList implements Iterable {

   private static class Node {
       Item item;
       Node next;
   }

   private Node head;
   
   ...
   
   public class HeadToTailIterator implements Iterator {
   
      Node node = head;
      
      public boolean hasNext() {
         return (node != null);
      }
      
      public Item next() {
         Item itemToReturn = node.item;
         node = node.next;
         return itemToReturn;
      }
   }
   
   public Iterator iterator() {
      return new HeadToTailIterator();
   }
}

Original images and some text by Victor S.Adamchik, CMU, 2009; adapted by P. Oser