Skip to content
intermediatePhase 13 · Java Collections

Deque & Stack

Use ArrayDeque for stack/queue operations and understand the Stack class.

45m
3 problems
Topic Progress0%

ArrayDeque

ArrayDeque

ArrayDeque is a resizable array implementation of the Deque interface. It is more efficient than LinkedList for stack and queue operations due to better cache locality.

Key characteristics:

  • Resizable circular array
  • O(1) for add/remove at both ends
  • Not synchronized
  • No capacity restrictions (grows as needed)
  • Faster than LinkedList for stack/queue
import java.util.*;

public class ArrayDequeDemo {
    public static void main(String[] args) {
        // Creating ArrayDeque
        Deque<String> deque = new ArrayDeque<>();

        // Adding elements
        deque.addFirst("B");  // [B]
        deque.addLast("C");   // [B, C]
        deque.addFirst("A");  // [A, B, C]
        deque.addLast("D");   // [A, B, C, D]
        System.out.println("Deque: " + deque);

        // Accessing elements
        System.out.println("First: " + deque.getFirst()); // A
        System.out.println("Last: " + deque.getLast()); // D
        System.out.println("peekFirst: " + deque.peekFirst()); // A
        System.out.println("peekLast: " + deque.peekLast()); // D

        // Removing elements
        System.out.println("removeFirst: " + deque.removeFirst()); // A
        System.out.println("removeLast: " + deque.removeLast()); // D
        System.out.println("After removes: " + deque); // [B, C]

        // Adding with offer (queue-style)
        deque.offer("E");   // addLast
        deque.offerFirst("F"); // addFirst
        System.out.println("After offers: " + deque); // [F, B, C, E]

        // Removing with poll (queue-style)
        System.out.println("poll: " + deque.poll()); // F (removeFirst)
        System.out.println("pollLast: " + deque.pollLast()); // E
        System.out.println("After polls: " + deque); // [B, C]

        // Size and emptiness
        System.out.println("Size: " + deque.size());
        System.out.println("isEmpty: " + deque.isEmpty());
        System.out.println("Contains B: " + deque.contains("B"));

        // Iterating
        System.out.println("\nForward:");
        for (String s : deque) {
            System.out.print(s + " ");
        }
        System.out.println("\nBackward:");
        Iterator<String> rit = deque.descendingIterator();
        while (rit.hasNext()) {
            System.out.print(rit.next() + " ");
        }
        System.out.println();
    }
}

Internal implementation: ArrayDeque uses a circular array with two pointers (head and tail). When elements are added/removed, the pointers wrap around the array using modular arithmetic. The array doubles in size when full.

Stack Operations

Stack Operations with ArrayDeque

ArrayDeque implements LIFO (Last-In-First-Out) stack operations using push, pop, and peek.

import java.util.*;

public class StackOperationsDemo {
    public static void main(String[] args) {
        // ArrayDeque as Stack
        Deque<Integer> stack = new ArrayDeque<>();

        // push - add to top (front)
        stack.push(10);  // [10]
        stack.push(20);  // [20, 10]
        stack.push(30);  // [30, 20, 10]
        System.out.println("Stack: " + stack);

        // peek - view top without removing
        System.out.println("Peek: " + stack.peek()); // 30
        System.out.println("Size after peek: " + stack.size()); // 3

        // pop - remove from top
        System.out.println("Pop: " + stack.pop()); // 30
        System.out.println("Pop: " + stack.pop()); // 20
        System.out.println("After pops: " + stack); // [10]

        // isEmpty - check if empty
        System.out.println("isEmpty: " + stack.isEmpty()); // false

        // Practical: Balanced parentheses
        System.out.println("\nBalanced: " + isBalanced("((()))")); // true
        System.out.println("Balanced: " + isBalanced("(()")); // false
        System.out.println("Balanced: " + isBalanced("{[()]}") ); // true
        System.out.println("Balanced: " + isBalanced("([)]")); // false

        // Practical: Reverse a string using stack
        System.out.println("Reversed: " + reverseString("Hello")); // olleH

        // Practical: Evaluate postfix expression
        System.out.println("Postfix: " + evaluatePostfix("3 4 + 2 *")); // 14
    }

    public static boolean isBalanced(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for (char c : s.toCharArray()) {
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            } else if (c == ')' || c == '}' || c == ']') {
                if (stack.isEmpty()) return false;
                char top = stack.pop();
                if ((c == ')' && top != '(') ||
                    (c == '}' && top != '{') ||
                    (c == ']' && top != '[')) {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }

    public static String reverseString(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for (char c : s.toCharArray()) {
            stack.push(c);
        }
        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.toString();
    }

    public static int evaluatePostfix(String expr) {
        Deque<Integer> stack = new ArrayDeque<>();
        for (String token : expr.split(" ")) {
            switch (token) {
                case "+": stack.push(stack.pop() + stack.pop()); break;
                case "-": {
                    int b = stack.pop(), a = stack.pop();
                    stack.push(a - b); break;
                }
                case "*": stack.push(stack.pop() * stack.pop()); break;
                default: stack.push(Integer.parseInt(token));
            }
        }
        return stack.pop();
    }
}

Key difference from Stack class: ArrayDeque's push/pop operate on the front (head) of the deque, which is O(1). The old java.util.Stack class is synchronized and slower.

Queue Operations

Queue Operations with ArrayDeque

ArrayDeque implements FIFO (First-In-First-Out) queue operations.

import java.util.*;

public class QueueOperationsDemo {
    public static void main(String[] args) {
        // ArrayDeque as Queue
        Queue<String> queue = new ArrayDeque<>();

        // offer - add to back (tail)
        queue.offer("Job1");
        queue.offer("Job2");
        queue.offer("Job3");
        System.out.println("Queue: " + queue); // [Job1, Job2, Job3]

        // peek - view front without removing
        System.out.println("Peek: " + queue.peek()); // Job1
        System.out.println("Size after peek: " + queue.size()); // 3

        // poll - remove from front
        System.out.println("Poll: " + queue.poll()); // Job1
        System.out.println("Poll: " + queue.poll()); // Job2
        System.out.println("After polls: " + queue); // [Job3]

        // offerFirst, offerLast - explicit ends
        Deque<String> deque = new ArrayDeque<>();
        deque.offerFirst("Front");
        deque.offerLast("Back");
        System.out.println("Deque: " + deque); // [Front, Back]

        // pollFirst, pollLast - remove from specific end
        System.out.println("pollFirst: " + deque.pollFirst()); // Front
        System.out.println("pollLast: " + deque.pollLast()); // Back

        // Practical: BFS using queue
        System.out.println("\nBFS traversal:");
        bfs(new int[][]{{1,2},{3,4},{5,6}}, 0);

        // Practical: Sliding window maximum
        int[] arr = {1, 3, -1, -3, 5, 3, 6, 7};
        System.out.println("\nSliding window max (k=3): " + 
            Arrays.toString(slidingWindowMax(arr, 3)));
    }

    // BFS traversal
    public static void bfs(int[][] graph, int start) {
        Queue<Integer> queue = new ArrayDeque<>();
        boolean[] visited = new boolean[graph.length];
        queue.offer(start);
        visited[start] = true;
        while (!queue.isEmpty()) {
            int node = queue.poll();
            System.out.print(node + " ");
            for (int neighbor : graph[node]) {
                if (!visited[neighbor]) {
                    queue.offer(neighbor);
                    visited[neighbor] = true;
                }
            }
        }
    }

    // Sliding window maximum
    public static int[] slidingWindowMax(int[] nums, int k) {
        Deque<Integer> deque = new ArrayDeque<>();
        int[] result = new int[nums.length - k + 1];
        for (int i = 0; i < nums.length; i++) {
            while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
                deque.pollFirst();
            }
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }
            deque.offerLast(i);
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }
        return result;
    }
}

Queue method summary:

  • offer(e) / add(e) — insert at tail
  • poll() / remove() — remove from head
  • peek() / element() — view head
  • All have O(1) time complexity in ArrayDeque

vs Stack

ArrayDeque vs Stack Class

Java has a legacy Stack class, but ArrayDeque is the recommended replacement.

Aspect ArrayDeque Stack
Synchronization No (faster) Yes (slower)
Implements Deque interface Vector + Stack
Performance O(1) for all ops O(1) but synchronized
Thread safety No Yes
Flexibility Stack + Queue Stack only
import java.util.*;

public class ArrayDequeVsStackDemo {
    public static void main(String[] args) {
        // Legacy Stack class (avoid)
        Stack<Integer> legacyStack = new Stack<>();
        legacyStack.push(1);
        legacyStack.push(2);
        legacyStack.push(3);
        System.out.println("Stack peek: " + legacyStack.peek()); // 3
        System.out.println("Stack pop: " + legacyStack.pop()); // 3
        System.out.println("Stack: " + legacyStack); // [1, 2]

        // Recommended: ArrayDeque
        Deque<Integer> modernStack = new ArrayDeque<>();
        modernStack.push(1);
        modernStack.push(2);
        modernStack.push(3);
        System.out.println("Deque peek: " + modernStack.peek()); // 3
        System.out.println("Deque pop: " + modernStack.pop()); // 3
        System.out.println("Deque: " + modernStack); // [1, 2]

        // ArrayDeque also works as Queue
        Queue<Integer> queue = new ArrayDeque<>();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        System.out.println("\nQueue poll: " + queue.poll()); // 1

        // Why ArrayDeque is better:
        System.out.println("\n--- Why ArrayDeque is better ---");
        System.out.println("1. Not synchronized - better performance");
        System.out.println("2. Implements Deque - can be stack AND queue");
        System.out.println("3. Better cache locality than LinkedList");
        System.out.println("4. No capacity limit (grows dynamically)");
        System.out.println("5. Part of modern Collections Framework");

        // Performance comparison
        long start = System.nanoTime();
        for (int i = 0; i < 1_000_000; i++) {
            legacyStack.push(i);
            legacyStack.pop();
        }
        long stackTime = System.nanoTime() - start;

        start = System.nanoTime();
        for (int i = 0; i < 1_000_000; i++) {
            modernStack.push(i);
            modernStack.pop();
        }
        long dequeTime = System.nanoTime() - start;

        System.out.println("\n1M push/pop - Stack: " + stackTime / 1_000_000 + "ms");
        System.out.println("1M push/pop - ArrayDeque: " + dequeTime / 1_000_000 + "ms");
    }
}

Rule: Never use java.util.Stack. Always use ArrayDeque for stack operations. It is faster, more flexible, and follows modern Java conventions.

Practice Problems

0/3solved
Valid Parentheses

Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid using ArrayDeque as a stack.

Solution
import java.util.*;

public class ValidParentheses {
    public static boolean isValid(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for (char c : s.toCharArray()) {
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            } else {
                if (stack.isEmpty()) return false;
                char top = stack.pop();
                if ((c == ')' && top != '(') ||
                    (c == '}' && top != '{') ||
                    (c == ']' && top != '[')) {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }
}
Implement Queue Using Two Stacks

Implement a queue using two ArrayDeque instances as stacks.

Solution
import java.util.*;

public class QueueUsingStacks {
    private Deque<Integer> stack1;
    private Deque<Integer> stack2;

    public QueueUsingStacks() {
        stack1 = new ArrayDeque<>();
        stack2 = new ArrayDeque<>();
    }

    public void enqueue(int x) {
        stack1.push(x);
    }

    public int dequeue() {
        if (stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}
Reverse a Queue

Write a method to reverse a queue using ArrayDeque as a stack.

Solution
import java.util.*;

public class QueueReverser {
    public static <T> Queue<T> reverse(Queue<T> queue) {
        Deque<T> stack = new ArrayDeque<>();
        while (!queue.isEmpty()) {
            stack.push(queue.poll());
        }
        Queue<T> reversed = new ArrayDeque<>();
        while (!stack.isEmpty()) {
            reversed.offer(stack.pop());
        }
        return reversed;
    }
}

Quiz

1. What is the main advantage of ArrayDeque over Stack?

Question 1 options

2. Which method adds an element to the front of an ArrayDeque?

Question 2 options

3. What is the time complexity of push, pop, and peek on ArrayDeque?

Question 3 options

4. What is the primary purpose of ArrayDeque as Stack and Queue?

Question 4 options

Flashcards

Question

Why is ArrayDeque preferred over Stack class?

Answer

ArrayDeque is not synchronized (faster), implements Deque interface (stack + queue), has better cache locality, and is the modern replacement for Stack. Never use java.util.Stack.

Question

What methods does ArrayDeque provide for stack operations?

Answer

push(e) adds to front, pop() removes from front, peek() views front. All are O(1). The front of the deque is the top of the stack.

Question

What is a circular array and how does ArrayDeque use it?

Answer

A circular array wraps around when it reaches the end. ArrayDeque uses head and tail pointers with modular arithmetic to efficiently add/remove at both ends without shifting elements.

Question

What is ArrayDeque as Stack and Queue?

Answer

ArrayDeque as Stack and Queue is a key concept in Java programming.

Question

When to use ArrayDeque as Stack and Queue?

Answer

Use ArrayDeque as Stack and Queue when building production systems that require reliability, scalability, and maintainability.

Revision Notes

Key Takeaways

  • 1.ArrayDeque is the recommended replacement for Stack class
  • 2.It provides O(1) operations at both ends
  • 3.Works as both stack (LIFO) and queue (FIFO)
  • 4.Better cache locality than LinkedList for stack/queue operations

Interview Tips

  • Always use ArrayDeque instead of Stack in interviews
  • Explain the circular array internal implementation
  • Know the difference between push/pop (stack) and offer/poll (queue)
  • Be ready to implement a queue using two stacks

Cheat Sheet

ArrayDeque Cheat Sheet

Structure

  • Resizable circular array
  • O(1) add/remove at both ends
  • Not synchronized

Stack Operations

  • push(e) → addFirst
  • pop() → removeFirst
  • peek() → peekFirst

Queue Operations

  • offer(e) → addLast
  • poll() → removeFirst
  • peek() → peekFirst

Deque Operations

  • addFirst/addLast → O(1)
  • removeFirst/removeLast → O(1)
  • peekFirst/peekLast → O(1)

vs Stack

  • ArrayDeque: not synchronized, faster
  • Stack: synchronized, slower, legacy