Java Queue, Deque and PriorityQueue: ArrayDeque vs Stack
Java queues: offer, poll and peek versus add, remove and element, ArrayDeque as both stack and queue, why to avoid Stack, and how PriorityQueue orders a heap.
- Course: Java study plan
- Module: Collections framework
- Kind: Lesson
- Reading time: 12 min
- Runtime: OpenJDK 18
Which class should you use for a stack in Java?
Use ArrayDeque for a stack in Java, through its push, pop and peek methods. It is a circular array with O(1) operations at both ends, faster than both Stack and LinkedList. The legacy java.util.Stack extends Vector, so every method is synchronised and it exposes index methods that break the stack abstraction; its own Javadoc recommends Deque instead.
Lesson
Queues process elements in an order decided by the structure: first-in-first-out for a plain queue, last-in-first-out for a stack, smallest-first for a priority queue. Java's Queue and Deque interfaces, ArrayDeque and PriorityQueue cover all three — and the legacy Stack class is the one you must know exists so you can avoid it. This lesson covers the interfaces' two method families, the implementations, and the algorithms (BFS, balanced brackets, top-k, scheduling) each is built for.
Queue: two method families
Every Queue operation comes in a throwing form and a returning form:
| Purpose | Throws on failure | Returns special value |
|---|---|---|
| Insert | add(e) → IllegalStateException if full | offer(e) → false |
| Remove head | remove() → NoSuchElementException if empty | poll() → null |
| Examine head | element() → NoSuchElementException | peek() → null |
Use offer/poll/peek for ordinary code — an empty queue is a normal condition, not an exception. The throwing forms are for bounded queues where "full" is a programming error. poll returning null means queues should not hold null elements (ArrayDeque forbids them; LinkedList allows and confuses).
Deque: both ends
Deque (double-ended queue) adds addFirst/addLast, offerFirst/offerLast, pollFirst/pollLast, peekFirst/peekLast, plus stack aliases push (= addFirst), pop (= removeFirst), peek (= peekFirst).
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); stack.push(2);
stack.peek(); // 2
stack.pop(); // 2
Deque<String> queue = new ArrayDeque<>();
queue.offer("a"); queue.offer("b"); // offer = offerLast
queue.poll(); // "a" — poll = pollFirst
ArrayDeque is a circular array: O(1) at both ends, no nulls, no capacity limit (it doubles), faster than LinkedList for every deque operation and than Stack for every stack operation. It is the answer to "which class for a stack?" and "which class for a queue?".
Why not java.util.Stack
Stack extends Vector: every method synchronised (slow), inherits get(i)/insertElementAt that break the stack abstraction, and iterates bottom-to-top (surprising). It survives for compatibility; the Javadoc itself says to use Deque. Interviewers ask about it precisely to hear this.
Note the iteration difference: iterating an ArrayDeque used as a stack yields top-first (push adds at the front); iterating a Stack yields bottom-first.
Stack patterns
// balanced brackets
Deque<Character> st = new ArrayDeque<>();
for (char c : s.toCharArray()) {
if ("([{".indexOf(c) >= 0) st.push(c);
else {
if (st.isEmpty()) return false;
char open = st.pop();
if ("([{".indexOf(open) != ")]}".indexOf(c)) return false;
}
}
return st.isEmpty();
Also: evaluating postfix expressions, undo stacks, DFS without recursion, "next greater element" (a monotonic stack), matching tags. Any recursion can be made iterative with an explicit Deque of frames.
Queue patterns
// breadth-first search over a grid
Deque<int[]> q = new ArrayDeque<>();
boolean[][] seen = new boolean[rows][cols];
q.offer(new int[] {sr, sc}); seen[sr][sc] = true;
while (!q.isEmpty()) {
int[] cur = q.poll();
for (int[] d : DIRS) {
int nr = cur[0] + d[0], nc = cur[1] + d[1];
if (nr < 0 || nr >= rows || nc < 0 || nc >= cols || seen[nr][nc] || grid[nr][nc] == '#') continue;
seen[nr][nc] = true;
q.offer(new int[] {nr, nc});
}
}
BFS, level-order tree traversal, task pipelines, sliding-window maximum (a monotonic deque), producer–consumer (with the concurrent BlockingQueue — Module 17).
PriorityQueue
A binary heap over an array: offer and poll are O(log n), peek O(1), and poll always returns the smallest element by natural order or a supplied comparator.
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
PriorityQueue<Task> byDeadline = new PriorityQueue<>(Comparator.comparing(Task::deadline));
minHeap.offer(5); minHeap.offer(1); minHeap.offer(3);
minHeap.poll(); // 1
minHeap.peek(); // 3
Facts that trip people:
- Iteration is not sorted.
for (x : pq)andpq.toString()show heap order, not ascending order. To get elements in order,pollrepeatedly (destructive) or copy and sort. - No nulls; elements must be
Comparableor a comparator must be given. remove(Object)andcontainsare O(n).- Not thread-safe;
PriorityBlockingQueueis the concurrent version. - Changing an element's priority while it is in the queue corrupts the heap — remove, mutate, re-add.
Patterns: top-k (keep a min-heap of size k: offer each element, poll when size exceeds k — O(n log k)), merging k sorted streams, Dijkstra's algorithm, event simulation ordered by time, scheduling by deadline.
Choosing
| Need | Use |
|---|---|
| Stack (LIFO) | ArrayDeque via push/pop/peek |
| Queue (FIFO) | ArrayDeque via offer/poll/peek |
| Both ends | ArrayDeque |
| Smallest/largest first | PriorityQueue (+ reverseOrder for largest) |
| Blocking, between threads | ArrayBlockingQueue, LinkedBlockingQueue (Module 17) |
| Legacy code | Stack, LinkedList — read them, do not write them |
Interview angle
- "Which class would you use for a stack in Java?"
ArrayDeque—Stackis a synchronisedVectorwith a broken abstraction. - "
addvsoffer?"addthrows when it cannot insert;offerreturns false. Same forremove/pollandelement/peek. - "Is a
PriorityQueuesorted?" Only its head is guaranteed; iteration order is heap order. - "How do you get the k largest elements efficiently?" A min-heap of size k.
- "Why does
ArrayDequereject null?"poll/peekuse null to mean empty.
Key takeaways
offer/poll/peekfor everyday use;add/remove/elementthrow.ArrayDequeis the stack and the queue;StackandLinkedListare legacy for these roles.PriorityQueueis a heap: O(log n) offer/poll, smallest first, unsorted iteration, no nulls, do not mutate priorities in place.- Stacks for brackets/DFS/undo; queues for BFS/pipelines; heaps for top-k/scheduling/Dijkstra.
Common questions
What is the difference between offer and add in a Java Queue?
Both insert an element, but add throws IllegalStateException when a bounded queue is full, while offer returns false. The same pairing holds for remove and poll, and for element and peek, when the queue is empty. Use offer, poll and peek in everyday code.
Is a PriorityQueue sorted in Java?
No — only its head is guaranteed to be the smallest element. A PriorityQueue is a binary heap, so iterating or printing it shows heap order, not sorted order. To get the elements in order, poll repeatedly or copy them into a list and sort it.
How do you make a max-heap in Java?
Give PriorityQueue a reversed comparator: new PriorityQueue<>(Comparator.reverseOrder()). By default a PriorityQueue is a min-heap whose poll returns the smallest element; the reversed comparator makes it return the largest.
How do you find the k largest elements efficiently in Java?
Keep a min-heap of size k: offer each element to a PriorityQueue and poll whenever its size exceeds k. The heap then holds the k largest, and the whole pass costs O(n log k) rather than the O(n log n) of sorting everything.
Why does ArrayDeque not allow null elements?
Because poll and peek return null to mean the deque is empty. A stored null would be indistinguishable from no element at all, so ArrayDeque rejects nulls outright.
Exercises
Brackets and top-k
Read an integer n and n lines. Lines of the form check <text> are answered balanced or unbalanced for ()[]{} using an ArrayDeque stack. Lines of the form topk <k> <m> <m numbers> are answered with the k largest numbers in descending order, space-separated, found with a min-heap PriorityQueue of size at most k.
Example: 2 then check ([]{}) and topk 2 5 4 9 1 7 3 →
balanced
9 7Merge k sorted lists with a heap
Read k, then k sorted lists (each a length followed by its values). Merge them into one sorted sequence with a PriorityQueue of (value, listIndex, position) entries — push the head of each list, then repeatedly poll the smallest and push that list's next element. Print the merged values space-separated, then polls=<total polls>.
Example: 3 then 2 1 5, 1 3, 3 2 4 6 →
1 2 3 4 5 6
polls=6In this module: Collections framework
- The collections framework — the map and how to choose
- Lists — ArrayList, LinkedList and the List API
- Sets — HashSet, LinkedHashSet and TreeSet
- Maps — HashMap internals, TreeMap, LinkedHashMap and the modern API
- Queues, deques and PriorityQueue (this lesson)
- Sorting, Comparable and Comparator in depth
- Iterators, fail-fast behaviour and safe modification
- Immutable collections and the utility toolkit
- Checkpoint — Collections framework
← Maps — HashMap internals, TreeMap, LinkedHashMap and the modern API · Sorting, Comparable and Comparator in depth →