Skip to content
Java collections 5 min read

Stack

Stack is a classic Last-In, First-Out (LIFO) data structure — the last element you put in is the first one that comes back out. Java has had a Stack class since Java 1.0, and it remains useful for interview questions, undo/redo logic, expression parsing, and depth-first algorithms.

What is a Stack?

Think of a stack like a stack of plates: you always add to and remove from the top. In Java, java.util.Stack<E> extends Vector and adds five classic stack operations on top of it.

import java.util.Stack;

public class BasicStack {
    public static void main(String[] args) {
        Stack<String> stack = new Stack<>();

        stack.push("first");
        stack.push("second");
        stack.push("third");

        System.out.println(stack.pop());   // removes top
        System.out.println(stack.peek());  // reads top without removing
        System.out.println(stack.isEmpty());
    }
}

Output:

third
second
false

Core Stack Methods

MethodDescriptionReturns
push(E item)Pushes an element onto the topthe element pushed
pop()Removes and returns the top elementtop element
peek()Returns the top element without removing ittop element
isEmpty()Checks whether the stack has no elementsboolean
search(Object o)Returns 1-based position from the top (-1 if not found)int

Warning: pop() and peek() throw EmptyStackException if the stack is empty. Always guard with isEmpty() or a try-catch when needed.

push() and pop()

import java.util.Stack;

public class PushPop {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();

        for (int i = 1; i <= 5; i++) {
            stack.push(i);
        }
        System.out.println("Stack: " + stack);

        while (!stack.isEmpty()) {
            System.out.print(stack.pop() + " ");
        }
    }
}

Output:

Stack: [1, 2, 3, 4, 5]
5 4 3 2 1 

Notice how elements come out in reverse order — the LIFO principle in action.

search() counts positions from the top (1 = top, 2 = one below, and so on):

import java.util.Stack;

public class SearchDemo {
    public static void main(String[] args) {
        Stack<String> stack = new Stack<>();
        stack.push("a");
        stack.push("b");
        stack.push("c");  // top

        System.out.println(stack.search("c")); // 1
        System.out.println(stack.search("a")); // 3
        System.out.println(stack.search("z")); // -1 (not found)
    }
}

Output:

1
3
-1

Practical Example — Balanced Parentheses Checker

A classic Stack use-case is checking whether brackets in a string are balanced:

import java.util.Stack;

public class BalancedBrackets {
    public static boolean isBalanced(String s) {
        Stack<Character> stack = new Stack<>();
        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 void main(String[] args) {
        System.out.println(isBalanced("{[()]}")); // true
        System.out.println(isBalanced("{[(])}"));  // false
        System.out.println(isBalanced("((()"));    // false
    }
}

Output:

true
false
false

Practical Example — Reverse a String

import java.util.Stack;

public class ReverseString {
    public static String reverse(String input) {
        Stack<Character> stack = new Stack<>();
        for (char c : input.toCharArray()) {
            stack.push(c);
        }
        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(reverse("hello")); // olleh
    }
}

Output:

olleh

Under the Hood

Stack extends Vector, which means every operation is synchronized — protected by an intrinsic lock. That is thread-safe but slow under contention because even single-threaded code pays the lock cost.

Internally, Vector (and therefore Stack) stores elements in an Object[]. When the array fills up, the backing array doubles in size — just like ArrayList. The difference is that every Vector method acquires the monitor on this before touching the array.

This legacy design has a few consequences:

  • Single-threaded code: You pay synchronization overhead you don’t need.
  • Multi-threaded code: Compound operations like if (!stack.isEmpty()) stack.pop() are still NOT atomic, so the synchronization gives a false sense of safety.
  • Inheritance misuse: Because Stack extends Vector, callers can call Vector methods like add(0, element) or remove(0) and insert/remove from the bottom — violating LIFO semantics silently.

For these reasons, Stack is considered a legacy class. The Java documentation itself recommends using Deque implementations instead.

Modern Alternative — Deque as a Stack

ArrayDeque is the preferred replacement for Stack in modern Java. It is unsynchronized (faster for single-threaded use), avoids the Vector inheritance pitfall, and implements the Deque interface which provides explicit stack methods.

import java.util.ArrayDeque;
import java.util.Deque;

public class DequeAsStack {
    public static void main(String[] args) {
        Deque<String> stack = new ArrayDeque<>();

        // push to top
        stack.push("first");
        stack.push("second");
        stack.push("third");

        System.out.println(stack.peek()); // third
        System.out.println(stack.pop());  // third
        System.out.println(stack.size()); // 2
    }
}

Output:

third
third
2

Tip: Declare the variable as Deque<T> (the interface), not ArrayDeque<T>, so you can swap implementations without changing call sites.

Stack vs ArrayDeque — Quick Comparison

FeatureStackArrayDeque (as stack)
Thread-safeYes (synchronized)No
Performance (single-thread)SlowerFaster
LIFO guaranteeCan be broken via Vector APIEnforced via Deque API
Recommended for new codeNoYes
Packagejava.utiljava.util

For concurrent scenarios, prefer java.util.concurrent.LinkedBlockingDeque or manage your own locking with a ReentrantLock (see ReentrantLock).

When to Use Stack

Use Stack (or ArrayDeque) when you need:

  • Undo / redo operations (each action pushed; undo pops).
  • Depth-first search (DFS) traversal of graphs or trees.
  • Expression evaluation — converting infix to postfix, evaluating RPN.
  • Call stack simulation — mimicking recursion iteratively.
  • Backtracking algorithms — maze solving, N-Queens, Sudoku.

Note: For new code, always prefer ArrayDeque over Stack. Reserve Stack for legacy codebases or situations where its specific API is required.

Iterating Over a Stack

Because Stack extends Vector which extends AbstractList, you can iterate with an enhanced for-each loop or an Iterator. Iteration goes from bottom to top (index 0 first):

import java.util.Stack;

public class IterateStack {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(10);
        stack.push(20);
        stack.push(30);

        // Bottom to top (index order)
        for (int val : stack) {
            System.out.print(val + " ");
        }
        System.out.println();

        // Top to bottom (LIFO order) — pop into a list or use a loop
        while (!stack.isEmpty()) {
            System.out.print(stack.pop() + " ");
        }
    }
}

Output:

10 20 30 
30 20 10 

Warning: Never call stack.remove(index) or stack.add(index, element) on a Stack — these are inherited Vector methods that bypass LIFO semantics and can corrupt the logical order of your stack.

  • Deque & ArrayDeque — the modern, faster alternative to Stack for LIFO and FIFO operations
  • Vector — Stack’s parent class; understanding it explains Stack’s synchronization behavior
  • Queue & PriorityQueue — the complementary FIFO data structure
  • LinkedList — also implements Deque; useful when frequent insertions/removals are needed
  • ArrayList — the resizable-array sibling; helpful for comparing internal storage
  • Collections Framework — the big picture of all Java collection types
Last updated June 13, 2026
Was this helpful?