ArrayDeque is the recommended implementation for both a stack and a queue in single-threaded code. It implements Deque, so one class covers every operation at both ends in amortized O(1).
Why it beats the alternatives
- vs
Stack—StackextendsVector, so it is synchronized (lock overhead) and leaksListmethods that break the LIFO contract.ArrayDequeis unsynchronized and exposes only deque operations. - vs
LinkedList—LinkedListallocates a heap node per element and scatters them across memory, so every traversal is a cache-miss tour.ArrayDequestores elements in one contiguous array, which the CPU prefetcher loves. Fewer allocations, far better locality.
How it works
Internally it is a circular buffer: a power-of-two array with head and tail indices that wrap via masking (index & (capacity - 1)). Push/pop at either end just adjusts an index — no shifting. When full, it doubles the array and copies, giving amortized O(1) adds (a single add can be O(n), but the cost averages out across many adds, exactly like ArrayList).
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); stack.pop(); // LIFO: addFirst / removeFirst
Deque<Integer> queue = new ArrayDeque<>();
queue.offer(1); queue.poll(); // FIFO: addLast / removeFirst
| Operation | Best | Average | Worst | Note |
|---|---|---|---|---|
| push / pop / peek (stack) | O(1) | O(1) | O(n) | worst = resize copy; amortized O(1) |
| offer / poll / peek (queue) | O(1) | O(1) | O(n) | worst = resize copy; amortized O(1) |
| contains / remove(Object) | O(1) | O(n) | O(n) | linear scan |