Chapter 12: Stacks and Stack-Based Algorithms
12.02Beginner / Intermediate4 topics12 min1 interactive animation

Implementations

Three ways to implement a stack in Java, and why the legacy java.util.Stack class is usually avoided: an array, a linked list and ArrayDeque.

Learning goals

  • Implement a stack on an array and on a linked list
  • Use ArrayDeque as the standard stack
  • Explain what is wrong with java.util.Stack

Prerequisites

Stack modelLinked list basics
12.02.1

Array stack

An array stack keeps an int[] and a size: the top is data[size - 1]. Push writes at size and increments it; pop decrements it. Growing by doubling keeps push amortised O(1). Memory is compact and cache friendly.

Step through pushes and pops in the animation to see the array and size.

Java
1class ArrayStack {
2 private int[] data = new int[4];
3 private int size;
4 void push(int x) {
5 if (size == data.length) data = Arrays.copyOf(data, size * 2);
6 data[size++] = x;
7 }
8 int pop() {
9 if (size == 0) throw new NoSuchElementException("stack underflow");
10 return data[--size];
11 }
12 int peek() {
13 if (size == 0) throw new NoSuchElementException("stack underflow");
14 return data[size - 1];
15 }
16 boolean isEmpty() { return size == 0; }
17}

Array-backed stack: push, pop, peek, overflow and underflow

Interactive · edit the input

Your input

Try

data[] · capacity 3 · size 0

0
1
2
Setup

An empty stack backed by an array of 3. The top is data[size - 1]; push and pop only touch the end, so both are O(1).

Step 1 / 11
Java
1class ArrayStack {
2 private int[] data; private int size;
3 ArrayStack(int capacity) { data = new int[capacity]; }
4 void push(int x) {
5 if (size == data.length) data = Arrays.copyOf(data, data.length * 2); // resizable; a fixed stack rejects instead
6 data[size++] = x;
7 }
8 int pop() {
9 if (size == 0) throw new NoSuchElementException("stack underflow");
10 return data[--size];
11 }
12 int peek() {
13 if (size == 0) throw new NoSuchElementException("stack underflow");
14 return data[size - 1]; // look without removing
15 }
16 boolean isEmpty() { return size == 0; }
17}

Live state

size

0

capacity

3

No condition evaluated on this step
Execution trace (dry run table)Show 11 steps
StepLineStateWhat happens
1L3size=0 capacity=3An empty stack backed by an array of 3. The top is data[size - 1]; push and pop only touch the end, so both are O(1).
2L6op=push(5) size=1 top=5data[size++] = 5: 5 is the new top.
3L6op=push(8) size=2 top=8data[size++] = 8: 8 is the new top.
4L6op=push(3) size=3 top=3data[size++] = 3: 3 is the new top.
5L14op=peek() top=3 size=3peek() returns 3 without removing it: size stays 3.
6L10op=pop() returned=3 size=2Return data[--size] = 3: the most recently pushed value leaves first (LIFO). The slot is simply ignored from now on.
7L10op=pop() returned=8 size=1Return data[--size] = 8: the most recently pushed value leaves first (LIFO). The slot is simply ignored from now on.
8L10op=pop() returned=5 size=0Return data[--size] = 5: the most recently pushed value leaves first (LIFO). The slot is simply ignored from now on.
9L9op=pop() size=0 errors=1pop() on an empty stack: underflow. Java's ArrayDeque.pop throws NoSuchElementException; poll() would return null.
10L6op=push(7) size=1 top=7data[size++] = 7: 7 is the new top.
11L16size=1 errors=1 return=[7]Final stack (bottom to top): [7]; 1 rejected operation.
12.02.2

Linked list stack

A linked-list stack makes the top the head of a singly linked list: push inserts a new head, pop removes the head. Every operation is a true O(1) with no resizing, but each element costs a node object and the nodes are scattered in memory. It never needs to copy elements and never wastes spare capacity.

Use it when worst-case O(1) per operation matters more than speed and memory.

Java
1class LinkedStack {
2 private static class Node { int val; Node next; Node(int v, Node n) { val = v; next = n; } }
3 private Node top;
4 void push(int x) { top = new Node(x, top); }
5 int pop() {
6 if (top == null) throw new NoSuchElementException("stack underflow");
7 int v = top.val; top = top.next; return v;
8 }
9 boolean isEmpty() { return top == null; }
10}
12.02.3

ArrayDeque in Java

java.util.ArrayDeque is the standard stack in modern Java: a resizable circular array with O(1) amortised operations at both ends. Use it through the Deque interface: push, pop and peek work at the front (the top). It rejects null elements and is not thread-safe. It is usually faster than both Stack and LinkedList.

Declare the variable as Deque so the implementation can change.

Java
1Deque<Integer> stack = new ArrayDeque<>();
2stack.push(1); stack.push(2); stack.push(3);
3int top = stack.peek(); // 3
4int popped = stack.pop(); // 3
5boolean empty = stack.isEmpty();
12.02.4

Why legacy Stack is usually avoided

java.util.Stack extends Vector, so it inherits dozens of unrelated list methods (such as add(index, e) and get(i)) that break the LIFO contract, and every method is synchronised, which costs time you rarely need. Its push adds at the end, so iteration order is bottom to top, the opposite of ArrayDeque. The JDK documentation itself recommends Deque implementations instead.

Use ArrayDeque unless you are maintaining old code.

Java
1Stack<Integer> legacy = new Stack<>();
2legacy.push(1); legacy.push(2);
3System.out.println(legacy); // [1, 2]: bottom to top
4legacy.add(0, 99); // allowed, and violates the idea of a stack
5Deque<Integer> modern = new ArrayDeque<>();
6modern.push(1); modern.push(2);
7System.out.println(modern); // [2, 1]: top first

Practice ladder

Now try it yourself

Easy

  • Implement push and pop for an array stack
  • Which end of an ArrayDeque does push use?

Medium

  • Implement a linked-list stack with size()
  • List three problems with java.util.Stack

Advanced

  • Benchmark Stack, ArrayDeque and LinkedList as stacks for 10⁷ push/pop pairs

Key takeaways

  • Array stacks are compact; linked stacks never resize.
  • ArrayDeque (through Deque) is the default stack in Java.
  • Avoid java.util.Stack: legacy, synchronised and not LIFO-only.