Implementing addFirst and addLast

Both add operations splice a new node between two existing nodes, so they use the insertBetween helper from SortedLinkedList in the List ADT chapter, here without a return value or a size update:

private void insertBetween(T value, Node<T> before, Node<T> after) {
  Node<T> node = new Node<>(value);
  node.prev = before;
  node.next = after;
  before.next = node;
  after.prev = node;
}

Both add operations start with the same contract check (a deque does not accept null) and then call the helper with the two nodes the new node goes between.

addFirst inserts the new node just after the front sentinel:

@Override
public void addFirst(T item) {
  if (item == null) {
    throw new IllegalArgumentException();
  }
  insertBetween(item, head, head.next);
}

addLast does the same thing at the other end:

@Override
public void addLast(T item) {
  if (item == null) {
    throw new IllegalArgumentException();
  }
  insertBetween(item, tail.prev, tail);
}