Why is ArrayDeque recommended over the legacy java.util.Stack class for stack behavior?
answer
- Stack extends Vector -> synchronized + index/middle operations leak in
- Stack iteration is bottom-to-top (surprising); abstraction is broken
- ArrayDeque = circular array, unsynchronized, O(1) both ends, good locality
- JDK Javadoc explicitly recommends ArrayDeque over Stack and over LinkedList
- Concurrent need -> ConcurrentLinkedDeque/BlockingDeque, NOT Stack
basics
~10 sStack is old and extends Vector, so every operation is synchronized (slow) and it exposes index-based methods that break stack semantics. ArrayDeque is faster, unsynchronized, and gives a clean push/pop/peek stack API.
solid answer
~50 sjava.util.Stack is a legacy class from Java 1.0 that extends Vector. That inheritance is its core flaw: every method is synchronized, adding lock overhead even in single-threaded code, and because it IS-A Vector it inherits index-based and middle-of-list operations (get(i), add(i, e), insertElementAt) that violate the LIFO abstraction a stack is supposed to enforce. Its iteration order is also bottom-to-top, the opposite of what most people expect when 'iterating a stack'. ArrayDeque, introduced in Java 6, implements Deque on a resizable circular array. Used as a stack via push/pop/peek it is faster (no synchronization, better memory locality than a linked structure), exposes only deque operations, and the Java documentation explicitly recommends it over Stack. The one caveat: ArrayDeque is not thread-safe, so for genuine concurrent use you'd reach for a concurrent structure (e.g. ConcurrentLinkedDeque or a BlockingDeque), not Stack either.
code
java · 11 lines// Legacy: synchronized, leaks Vector operations
Stack<Integer> legacy = new Stack<>();
legacy.push(1);
legacy.add(0, 99); // index op from Vector -> breaks LIFO abstraction!
// Preferred: clean, fast, unsynchronized stack
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1);
stack.push(2);
int top = stack.pop(); // 2, LIFO
// stack.get(0); // does not compile - no index accessgo deeper
Knows ArrayDeque is the modern, faster choice and Stack is old; can use push/pop/peek.
Explains that Stack extends Vector (synchronized, leaks index operations, odd iteration order) and that ArrayDeque is an unsynchronized circular array.
Discusses performance (locality, amortized O(1), lock overhead), the JDK recommendation, the null difference, and that Stack's synchronization is insufficient anyway.
Frames it as an abstraction/inheritance design lesson (Stack IS-A Vector is bad LSP/composition), and prescribes the right concurrent alternatives (ConcurrentLinkedDeque/BlockingDeque) for real concurrency needs.
## The two classes **`java.util.Stack<E>`** is a class from Java 1.0 that models a LIFO stack with `push`, `pop`, `peek`, `empty`, and `search`. **`java.util.ArrayDeque<E>`** is a Java 6 class implementing the `Deque` interface; used as a stack it offers `push`, `pop`, and `peek`. Modern Java guidance and the JDK Javadoc itself recommend `ArrayDeque` over `Stack`. ## Why Stack is considered legacy ### 1. It extends Vector `Stack extends Vector`. `Vector` is a Java 1.0 resizable array whose every public method is **`synchronized`** (it acquires the object's monitor lock on each call). Consequences: - **Unnecessary locking overhead.** Even in single-threaded code you pay for synchronization on every `push`/`pop`. - **Broken abstraction.** Because `Stack` IS-A `Vector`, it inherits operations that have nothing to do with a stack: `get(int)`, `add(int, E)`, `insertElementAt`, `remove(int)`, `set(int, E)`. A caller can reach into the middle of the 'stack' and mutate it, defeating the LIFO contract. A clean stack should expose only push/pop/peek. - **Surprising iteration order.** Iterating a `Stack` (it IS-A List) goes **bottom-to-top** (insertion order), which is the opposite of popping order. People expect to iterate from the top. ### 2. Synchronization is the wrong default Single-thread synchronization is pure cost with no benefit, and the per-method locking of `Vector`/`Stack` is *not* even sufficient for compound operations (check-then-act still needs external locking), so it gives a false sense of safety while charging for it. ## Why ArrayDeque is better `ArrayDeque` is backed by a **resizable circular array** (a single array with head/tail indices that wrap around). Benefits: - **No synchronization** — no per-operation lock overhead in the common single-threaded case. - **Good cache locality** — contiguous array storage beats the pointer-chasing of a linked structure; amortized O(1) push/pop at both ends. - **Clean API** — as a `Deque` it exposes only deque/stack operations; you cannot index into the middle, so the LIFO/FIFO abstraction holds. - **Versatile** — the same class is an efficient FIFO queue *and* a LIFO stack. - **Recommended by the JDK** — the `Deque` and `ArrayDeque` Javadoc explicitly say it is likely faster than `Stack` when used as a stack (and faster than `LinkedList` when used as a queue). ## The important caveat: thread safety `ArrayDeque` is **not** synchronized. If multiple threads mutate it without external synchronization the behavior is undefined. But the answer to 'I need a concurrent stack' is **not** to fall back to `Stack` — it is to use a purpose-built concurrent structure such as `java.util.concurrent.ConcurrentLinkedDeque` (lock-free) or a `BlockingDeque` implementation. `Stack`'s coarse synchronization is both slow and insufficient for real concurrent algorithms. ## nulls Like other deques, `ArrayDeque` **forbids null** elements (they would collide with the null-means-empty signal of `peek`/`poll`), throwing `NullPointerException`. `Stack`/`Vector` *do* allow null, another subtle behavioral difference. ## One-line summary Prefer `ArrayDeque` (clean, fast, unsynchronized) for single-threaded stacks and queues; use a `java.util.concurrent` deque when you genuinely need concurrency. Treat `Stack` (and raw `Vector`) as legacy.
- If you genuinely need a thread-safe stack/deque, what should you use instead of Stack?A concurrent structure such as ConcurrentLinkedDeque (lock-free, non-blocking) or a BlockingDeque (e.g. LinkedBlockingDeque) when you need blocking/capacity semantics. Stack's Vector-level synchronization is slower and still insufficient for compound check-then-act operations.
- What is the data structure behind ArrayDeque and why does it help performance?A resizable circular array with head/tail indices that wrap around. Contiguous storage gives good cache locality and amortized O(1) insertion/removal at both ends, beating the pointer-chasing of linked alternatives.
saying these in an interview costs you the question
- Recommending Stack because it is 'thread-safe' (its locking is slow and insufficient for compound ops)
- Saying ArrayDeque is thread-safe (it is not)
- Claiming ArrayDeque allows null elements (it forbids them)
- Not knowing Stack extends Vector, which is the root of the criticism