Implementing LinkedQueue

Here is the Queue interface:

public interface Queue<T> {
  void enqueue(T item);  // throws IllegalArgumentException if item is null
  void dequeue();        // throws NoSuchElementException if the queue is empty
  T front();             // throws NoSuchElementException if the queue is empty
  boolean isEmpty();
}

LinkedQueue<T> implements the Queue<T> interface with a singly linked list of nodes. The front of the queue is at the head, and the back is at the tail:

import java.util.NoSuchElementException;

public class LinkedQueue<T> implements Queue<T> {

  private Node<T> head;  // the front of the queue, or null when empty
  private Node<T> tail;  // the back of the queue, or null when empty

  private static class Node<T> {
    T value;
    Node<T> next;

    Node(T value) {
      this.value = value;
    }
  }

  public LinkedQueue() {
    head = null;
    tail = null;
  }

  // enqueue, dequeue, front, and isEmpty go here
}
Solution

Not on a singly linked list. dequeue would remove the tail, which needs the node before it, and reaching that node means traversing from the head. On a doubly linked list, the tail can move to the previous node without a traversal, so every operation is .

Solution

dequeue must also change tail when it removes the only item: afterwards head and tail are both null. enqueue must also change head when the queue is empty: afterwards head and tail both point at the new node.

Solution
public boolean isEmpty() {
  return head == null;
}

public T front() {
  if (isEmpty()) {
    throw new NoSuchElementException();
  }
  return head.value;
}

public void dequeue() {
  if (isEmpty()) {
    throw new NoSuchElementException();
  }
  head = head.next;
  if (head == null) {
    tail = null;
  }
}
Solution
public void enqueue(T item) {
  if (item == null) {
    throw new IllegalArgumentException();
  }
  Node<T> newNode = new Node<>(item);
  if (isEmpty()) {
    head = newNode;
  } else {
    tail.next = newNode;
  }
  tail = newNode;
}
Solution

All four are in the worst case: each one reads or changes only head, tail, and at most one node.