Queue, Deque & Stack
Queue, Deque & Stack
A Queue is a collection designed for holding elements prior to processing. The defining rule is FIFO — First In, First Out: elements are added at one end (the tail) and removed from the other end (the head). Think of a checkout line at a supermarket.
A Deque (double-ended queue, pronounced "deck") generalises the queue: you can add and remove elements at both ends. This single abstraction can act as a queue, a stack, or a sliding-window buffer.
The Queue Interface
java.util.Queue<E> extends Collection and adds two families of methods — one that throws exceptions on failure and one that returns a special value:
- Tail (add):
add(e)throwsIllegalStateException;offer(e)returnsfalse. - Head (remove):
remove()throwsNoSuchElementException;poll()returnsnull. - Head (peek):
element()throws;peek()returnsnull.
In practice, prefer the offer / poll / peek trio — they are null-safe and the idiomatic choice for most queues.
Queue interface was designed to cover bounded queues (like ArrayBlockingQueue) where capacity limits can prevent insertion. The exception-throwing variants signal a programming error; the special-value variants are for flow control where a full or empty queue is a normal condition.
The Deque Interface
java.util.Deque<E> extends Queue and mirrors every method at both ends. The naming convention is clear: methods end in First or Last.
offerFirst(e)/offerLast(e)— add at head / tail.pollFirst()/pollLast()— remove from head / tail, returnnullif empty.peekFirst()/peekLast()— inspect without removing.push(e)/pop()/peek()— stack aliases that operate on the head.
ArrayDeque — the Go-To Implementation
java.util.ArrayDeque<E> is a resizable circular array that implements Deque. It is the right default choice for both queues and stacks in single-threaded code.
Using ArrayDeque as a Queue (FIFO)
Declare the variable as Queue<String>, not ArrayDeque<String>. Programming to the interface keeps your code flexible — you can swap in a LinkedList or a bounded ArrayBlockingQueue without changing any other line.
Using ArrayDeque as a Stack (LIFO)
A stack follows LIFO — Last In, First Out. The classic use-cases are undo history, expression parsing, and depth-first traversal.
Declare the variable as Deque<Integer> when you intend stack semantics. The push / pop / peek aliases on Deque operate exclusively on the head, which is exactly what a stack needs.
Why Avoid java.util.Stack?
Java ships a class called java.util.Stack<E> that has been in the JDK since version 1.0. You should not use it in new code.
StackextendsVector, which synchronises every method. In single-threaded code that lock acquisition is pure overhead with zero benefit.- Because
StackextendsVector, it inheritsget(int index),add(int index, E e), and other indexed-access methods that break the stack abstraction. Callers can insert or remove elements anywhere — that is not a stack. - The Javadoc for
Stackitself says: "A more complete and consistent set of LIFO stack operations is provided by theDequeinterface and its implementations, which should be used in preference to this class."
Deque<E> stack = new ArrayDeque<>(); instead. It is faster (no synchronisation), exposes only stack-appropriate operations via the Deque interface type, and is the recommendation in the official Javadoc.
Performance Characteristics
ArrayDeque stores elements in a circular array. All four boundary operations — add/remove at head or tail — are amortised O(1). The array doubles when full, which is an occasional O(n) copy, but averaged across all insertions the cost is O(1). There is no per-node allocation overhead as with a linked structure.
LinkedList also implements Deque, but each element is a separate heap object with two pointer fields. For queue and stack workloads, ArrayDeque uses less memory and has better cache locality, making it faster in practice. Prefer ArrayDeque unless you specifically need the List interface on the same object.
new ArrayDeque<>(1024) pre-allocates the internal array, avoiding resize copies during the fill phase. The default is 16 elements.
A Practical Example: Balanced Brackets Checker
Checking whether brackets in a string are balanced is a textbook stack problem. Each opening bracket is pushed; each closing bracket must match the top of the stack.
Choosing Between Queue and Deque
- Need strict FIFO processing? Declare as
Queue<E>, back it withArrayDeque. - Need a stack (LIFO)? Declare as
Deque<E>, back it withArrayDeque, usepush/pop/peek. - Need to manipulate both ends (e.g. a sliding window)? Use
Deque<E>with the fullFirst/LastAPI. - Need thread safety? Look at
java.util.concurrent:LinkedBlockingDeque,ConcurrentLinkedQueue, etc.
Summary
Queue defines FIFO semantics; Deque generalises it to both ends and doubles as a stack. ArrayDeque is the correct implementation for both roles in single-threaded code — it is faster and leaner than LinkedList for boundary-only operations. Always declare variables as Queue<E> or Deque<E>, never as the concrete type. And never reach for the legacy Stack class: the Javadoc itself points you to Deque.