Flat Preloader Icon

DS Stack

What Is A Stack ?

In Java, a stack is a linear data structure that follows the Last-In-First-Out (LIFO) principle. It means that the most recently added element is the first one to be removed. You can think of a stack as a collection of elements with two main operations:

  1. Push: This operation is used to add an element to the top of the stack.

  2. Pop: This operation is used to remove the top element from the stack.

Additionally, there’s often a third operation:

  1. Peek (or Top): This operation allows you to view the element at the top of the stack without removing it.

In Java, you can implement a stack using various data structures, but one of the most common approaches is to use the java.util.Stack class, which is part of the Java Standard Library. It extends the Vector class and provides all the necessary methods for working with a stack, including push(), pop(), and peek().

Here’s a simple example of using a stack in Java:

				
					import java.util.Stack;

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

        // Push elements onto the stack
        stack.push(1);
        stack.push(2);
        stack.push(3);

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

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

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

				
			

Key Points of Stack

  1. LIFO (Last-In, First-Out): A stack follows the LIFO principle, which means that the last element added to the stack is the first one to be removed.

  2. Data Types: In Java, a stack can hold elements of any data type, including primitive types and objects. It is typically implemented using generic classes to provide flexibility.

  3. Core Operations:

    • push(element): Adds an element to the top of the stack.
    • pop(): Removes and returns the top element from the stack.
    • peek(): Returns the top element without removing it.
    • isEmpty(): Checks if the stack is empty.
    • size(): Returns the number of elements in the stack.
  4. Java Stack Implementation: You can implement a stack in Java using various data structures. The most common choices are an array-based implementation or a linked list-based implementation. Java provides the java.util.Stack class, but it’s recommended to use the more modern java.util.Deque (Double-Ended Queue) interface or its implementing class java.util.ArrayDeque for better performance and versatility.

  5. Exception Handling: When popping from an empty stack or pushing to a stack with insufficient memory, Java may throw exceptions like EmptyStackException or OutOfMemoryError. It’s important to handle these exceptions appropriately.

  6. Use Cases: Stacks are widely used in Java for tasks like implementing function call stacks (used in recursion and method calls), parsing expressions, undo/redo functionality in applications, and more.

  7. Recursion: Java uses the call stack to manage recursive function calls. Each function call is pushed onto the call stack, and when a function returns, it’s popped from the stack. This allows Java to keep track of the execution flow.

  8. Stack Memory: In Java, there are two types of memory: heap memory (for objects) and stack memory (for method calls and local variables). Each thread in a Java application has its own stack memory, which is used to store method call information and local variables.

  9. Thread Safety: If multiple threads are accessing a stack concurrently, you may need to ensure thread safety by using synchronization mechanisms such as synchronized or concurrent collections like java.util.concurrent.ConcurrentLinkedDeque.

  10. Performance Considerations: Depending on the specific implementation, the performance of a stack can vary. Array-based stacks have constant-time complexity for most operations, while linked list-based stacks have O(1) complexity for push and pop, but with slightly higher overhead.

Working Of Stack

Initialization: To use a stack in Java, you typically import the java.util.Stack class and create an instance of it.

				
					import java.util.Stack;
Stack<Integer> stack = new Stack<>();

				
			

Pushing Elements: You can push (add) elements onto the stack using the push() method. The element is added to the top of the stack.

				
					stack.push(1);
stack.push(2);
stack.push(3);

				
			

The stack now contains [1, 2, 3], with 3 at the top.

Popping Elements: To remove the top element from the stack, you can use the pop() method. This returns the top element and removes it from the stack.

				
					int topElement = stack.pop(); // Removes and returns 3

				
			

After this operation, the stack contains [1, 2].

Peeking at the Top: If you want to look at the top element without removing it, you can use the peek() method.

				
					int topElement = stack.peek(); // Returns 2, 
but doesn't remove it from the stack

				
			

Checking if the Stack is Empty: You can use the isEmpty() method to check if the stack is empty.

				
					boolean isEmpty = stack.isEmpty(); // Returns false

				
			

Getting the Size: To get the number of elements in the stack, you can use the size() method.

				
					int size = stack.size(); // Returns 2
    
				
			

Iterating through the Stack: You can iterate through the elements in the stack using loops or other iterable mechanisms in Java.

				
					for (int element : stack) {
    System.out.println(element);
}

				
			

Fundamental Operations

  1. Push:

    • This operation is used to add an element to the top of the stack.
    • It increases the size of the stack by one.
    • The new element becomes the top element of the stack.
  2. Pop:

    • This operation is used to remove the top element from the stack.
    • It decreases the size of the stack by one.
    • The element that was removed is returned or discarded.
  3. Peek (or Top):

    • This operation retrieves the top element from the stack without removing it.
    • It does not change the size of the stack.
  4. IsEmpty:

    • This operation checks whether the stack is empty or not.
    • It returns a boolean value, typically true if the stack is empty and false if it’s not.

Let’s see these operations with some examples:

  • Stack using Linked List in Java
				
					class Node {
    int data;
    Node next;
    
    Node(int data) {
        this.data = data;
        this.next = null;
    }
}

class Stack {
    Node top;

    public void push(int item) {
        Node newNode = new Node(item);
        newNode.next = top;
        top = newNode;
    }

    public int pop() {
        if (top == null) {
            System.out.println("Stack is empty");
            return -1; 
            // Assuming -1 indicates an empty stack
        }
        int item = top.data;
        top = top.next;
        return item;
    }

    public int peek() {
        if (top == null) {
            System.out.println("Stack is empty");
            return -1;
        }
        return top.data;
    }

    public boolean isEmpty() {
        return top == null;
    }
}

// Create a stack
Stack stack = new Stack();

// Push elements onto the stack
stack.push(1);
stack.push(2);
stack.push(3);

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

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

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