JavaStack

Stack

A stack is a last-in-first-out (LIFO) data structure: the most recently added element is always the first one removed — think of a stack of plates. Java has a legacy class literally named java.util.Stack, but modern Java code almost always avoids it in favor of ArrayDeque used as a stack. Understanding why is just as useful as understanding the stack operations themselves.
The Legacy java.util.Stack Class
Stack has been part of Java since version 1.0. It offers the classic stack operations — push() to add an element, pop() to remove and return the top element, and peek() to look at the top element without removing it.

Basic java.util.Stack usage

Java
import java.util.Stack;

Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);

System.out.println(stack.peek()); // 3, top of the stack
System.out.println(stack.pop());  // removes and returns 3
System.out.println(stack);        // remaining: [1, 2]
3
3
[1, 2]
Stack extends Vector — and inherits synchronization overhead
java.util.Stack extends Vector, Java's original synchronized list class from before the Collections Framework existed. Every method on Stack is synchronized, meaning it acquires a lock on every call — even in single-threaded code that never needs thread safety at all. That's pure overhead you're paying for nothing in the vast majority of programs, and it's the main reason Stack is considered legacy today.
The Modern Alternative: ArrayDeque
The official recommendation — stated directly in the JDK documentation — is to use ArrayDeque instead of Stack for stack behavior when you don't need thread safety. ArrayDeque supports the same push()/pop()/peek() operations through the Deque interface, isn't synchronized, and is generally faster.

Stack vs. ArrayDeque, side by side

Java
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Stack;

// Legacy — works, but carries unneeded synchronization overhead
Stack<String> legacyStack = new Stack<>();
legacyStack.push("a");
legacyStack.push("b");
System.out.println(legacyStack.pop()); // "b"

// Modern — same LIFO behavior, no synchronization overhead
Deque<String> modernStack = new ArrayDeque<>();
modernStack.push("a");
modernStack.push("b");
System.out.println(modernStack.pop()); // "b"
b
b
Tip
Declare the variable as Deque<T>, not ArrayDeque<T> — it reads clearly as "this is being used as a stack/deque" and keeps your code flexible if you ever swap the implementation.
Worked Example: Balanced Parentheses

A classic use case for a stack is checking whether a string of brackets is balanced. Every opening bracket gets pushed; every closing bracket pops the top and checks that it matches.

Checking balanced parentheses with ArrayDeque

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

public class BalancedParens {
    static boolean isBalanced(String input) {
        Deque<Character> stack = new ArrayDeque<>();

        for (char c : input.toCharArray()) {
            if (c == '(' || c == '[' || c == '{') {
                stack.push(c);
            } else if (c == ')' || c == ']' || c == '}') {
                if (stack.isEmpty()) return false;
                char open = stack.pop();
                if ((c == ')' && open != '(') ||
                    (c == ']' && open != '[') ||
                    (c == '}' && open != '{')) {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }

    public static void main(String[] args) {
        System.out.println(isBalanced("{[a + (b * c)] - 1}")); // true
        System.out.println(isBalanced("(a + [b)]"));           // false
    }
}
true
false

Aspect

java.util.Stack

ArrayDeque as a stack

Base class

Extends Vector

Implements Deque directly

Synchronization

Every method synchronized (overhead even single-threaded)

Not synchronized

Performance

Slower due to locking

Faster in typical use

Status

Legacy, not recommended for new code

Recommended

Null elements

Allowed

Not allowed

  • Stack is LIFO: push() adds, pop() removes and returns the top, peek() inspects it

  • java.util.Stack extends Vector and synchronizes every method — unnecessary overhead for most code

  • ArrayDeque is the JDK-recommended modern replacement for stack behavior

  • Balanced-bracket checking is a classic, practical stack use case