Stack Class in Java

The Stack class is a powerful data structure available in Java that follows the LIFO principle. LIFO stands for “Last In, First Out,” meaning that the last element added to the stack is the first one to be removed. Think of it as a stack of books: the book you place on top is the one you can easily retrieve without disturbing the others. This characteristic makes the Stack class suitable for scenarios where you need to manage items in a specific order.

This guide will lead you through the essential aspects of the Stack class, regardless of whether you’re new to programming or an experienced developer looking to sharpen your skills.

The Anatomy of the Stack Class

Before we dive into the nitty-gritty details, let’s grasp the core attributes that make the Stack class an indispensable tool:

  • LIFO Ordering: The Stack class maintains the last-in, first-out ordering, making it ideal for tasks where the order of elements matters.
  • Push and Pop: You can push (add) elements onto the stack using the push(element) method and pop (remove) elements from the top using the pop() method.
  • Peek: The peek() method allows you to view the element at the top of the stack without removing it.
  • Synchronization: Similar to the Vector class, Stacks are synchronized and are considered thread-safe for multithreaded applications.
  • Legacy Usage: Although the Deque interface introduced in Java 6 provides a more modern alternative, the Stack class remains in use due to its legacy support.

Now that we’ve got an overview, let’s dig deeper into the functionalities of the Stack class and understand how it can simplify your Java programming tasks.

Creating and Using a Stack in Java

To start using the Stack class, you first need to create an instance of it. Here’s an example of how you can create a Stack and perform basic operations:

import java.util.Stack;

public class StackExample {
    public static void main(String[] args) {
        // Create a new Stack instance
        Stack<Integer> stack = new Stack<>();

        // Push elements onto the stack
        stack.push(10);
        stack.push(20);
        stack.push(30);

        // Pop elements from the stack
        int poppedElement = stack.pop();
        System.out.println("Popped element: " + poppedElement);

        // Peek at the top element
        int topElement = stack.peek();
        System.out.println("Top element: " + topElement);
    }
}

In the example above, we import the Stack class from the java.util package, create a new Stack of type Integer, and perform push and pop operations. The output will indicate the popped and top elements.

Common Stack Applications

Stacks are incredibly versatile and find application in various programming scenarios. Here are some common use cases:

  • Expression Evaluation: Stacks are used for evaluating arithmetic expressions by maintaining operators and operands.
  • Function Call Stack: In languages like Java, stacks manage function calls and returns during program execution.
  • Undo/Redo Functionality: Stacks can be used to implement undo and redo functionality in applications.
  • Backtracking: Stacks assist in backtracking algorithms, such as finding paths in mazes.

Advanced Usage: Implementing a Stack from Scratch

Understanding how the Stack class works under the hood can be enlightening. Let’s implement a basic stack using an array:

public class CustomStack<T> {
    private Object[] array;
    private int size;
    private int capacity;

    public CustomStack(int capacity) {
        this.capacity = capacity;
        array = new Object[capacity];
    }

    public void push(T element) {
        if (size == capacity) {
            throw new IllegalStateException("Stack is full");
        }
        array[size++] = element;
    }

    public T pop() {
        if (isEmpty()) {
            throw new IllegalStateException("Stack is empty");
        }
        return (T) array[--size];
    }

    public T peek() {
        if (isEmpty()) {
            throw new IllegalStateException("Stack is empty");
        }
        return (T) array[size - 1];
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public int size() {
        return size;
    }
}

Now, let’s use this class in our main class and execute:

public class Main {
    public static void main(String[] args) {
        // Create a new CustomStack with a capacity of 5
        CustomStack<Integer> stack = new CustomStack<>(5);

        // Push elements onto the stack
        stack.push(10);
        stack.push(20);
        stack.push(30);

        // Print the size of the stack
        System.out.println("Size of the stack: " + stack.size());

        // Peek at the top element
        System.out.println("Top element: " + stack.peek());

        // Pop elements from the stack
        int poppedElement1 = stack.pop();
        int poppedElement2 = stack.pop();

        // Print the popped elements
        System.out.println("Popped element 1: " + poppedElement1);
        System.out.println("Popped element 2: " + poppedElement2);

        // Check if the stack is empty
        System.out.println("Is the stack empty? " + stack.isEmpty());

        // Print the final size of the stack
        System.out.println("Size of the stack after pops: " + stack.size());
    }
}

Output:

Size of the stack: 3
Top element: 30
Popped element 1: 30
Popped element 2: 20
Is the stack empty? false
Size of the stack after pops: 1

FAQs about the Stack Class in Java

Is the Stack class efficient for all scenarios?

While the Stack class is efficient for specific tasks, it might not be the best choice for all scenarios. For instance, if you need to access elements in the middle frequently, a different data structure might be more suitable.

Can I use the Stack class in multithreaded applications?

Yes, the Stack class is synchronized and is considered safe for multithreaded environments. However, consider alternatives if synchronization is not required.

Are Stacks outdated in modern Java programming?

While newer alternatives like the Deque interface offer more functionalities, stacks remain relevant for their simplicity and specific use cases.

How can I check if a Stack is empty?

The isEmpty() method returns true if the stack is empty, and false otherwise.

What’s the primary difference between a Stack and a Queue?

While both are linear data structures, a Stack follows the LIFO principle, whereas a Queue follows the FIFO (First In, First Out) principle.

Can I implement a Stack using LinkedList instead of an array?

Absolutely! LinkedLists are also suitable for implementing a Stack in Java.