Skip to main content

Stack in Java

Stack in Java: Deep Dive & Backend Working

Official Java Definition

"The Stack class represents a last-in-first-out (LIFO) stack of objects. It extends class Vector with five operations that allow a vector to be treated as a stack." — Official Java Documentation

The Stack is one of the oldest legacy classes in the Java Collection Framework (java.util.Stack). To understand it completely, you must first understand its inheritance hierarchy, because its entire internal behavior is dictated by its parent class.

Iterable ↓ Collection ↓ List ↓ Vector ↓ Stack

1. What is a Stack? (The LIFO Principle)

A Stack strictly follows the LIFO protocol: Last In, First Out.

Imagine a stack of dinner plates. The last plate you place on top of the pile is unequivocally the first plate you will pick up when you need one. You cannot easily pull a plate from the middle without disrupting the entire stack.

Physical Concept

↓ TOP
30
20
10

Internal Array Mapping

10
20
30
_
_

Index 0 → Index (size - 1)


2. Internal Backend Structure

The most important fact about Stack is that it extends Vector. Because of this, it does not implement its own linked nodes. Instead, it utilizes a Dynamic Resizable Array under the hood.

It inherits two critical variables directly from Vector:

protected Object[] elementData; // The physical array storing the elements protected int elementCount; // The current number of elements (size)

Because it is backed by a contiguous array, it is exceptionally Cache Friendly. Modern CPUs can load the adjacent array memory into the L1/L2 cache, resulting in blazing fast raw iteration speed.


3. Backend Working: Core Operations

1. Pushing Data: push(E item)

When you push an item onto the stack, Java simply appends it to the very end of the elementData array.

public E push(E item) { addElement(item); // Calls Vector's synchronized method return item; }

Internal Flow: Check capacity → Resize if full → Insert at elementData[size]size++.
Time Complexity: Amortized $O(1)$.

2. Popping Data: pop()

This retrieves the item at the top of the stack and physically removes it.

public synchronized E pop() { int len = size(); E obj = peek(); // Get top item removeElementAt(len - 1); // Delete from end of array return obj; }

Time Complexity: $O(1)$. Because the element is removed from the absolute end of the array, no shifting is required.

3. Peeking Data: peek()

Simply returns the top element without removing it. It instantly accesses elementData[size - 1]. Complexity is $O(1)$.

4. Searching: search(Object o)

This returns the 1-based position from the TOP of the stack, not the array index. If the stack is [10, 20, 30], and you call search(20), it returns 2 (because 30 is position 1, 20 is position 2). Complexity is $O(n)$ because it must iterate to find the object.


4. Synchronization and Thread Safety

Because Stack extends Vector, every single inherited method is synchronized.

This means it relies heavily on the Object Monitor Lock. If Thread A is popping an element, Thread B is completely blocked from pushing an element. While this guarantees thread safety, it imposes a massive performance overhead. Acquiring and releasing locks slows down the CPU significantly, even in single-threaded applications.


5. Capacity Expansion & Memory

When the internal elementData array becomes completely full, the Stack relies on the Vector's growth strategy.

Growth Rule: It creates a new array using Arrays.copyOf() and multiplies the capacity by exactly 2x (100%).

Example: Array size 10 → 20 → 40 → 80.


6. The Legacy Flaw: Why Java Developers Avoid `Stack`

The Inheritance Design Flaw

By using extends Vector, the Stack class inherently inherited all of Vector's methods. This severely violates the pure LIFO principle.

Stack<Integer> stack = new Stack<>(); stack.add(0, 999); // Allowed! Inserts at the absolute bottom. stack.remove(1); // Allowed! Deletes from the middle.

Because a developer can arbitrarily insert or remove elements from the middle or bottom of the collection, the structural integrity of the Stack cannot be guaranteed.

The Official Modern Replacement: `ArrayDeque`

The official Java documentation explicitly advises against using the Stack class. Instead, modern Java dictates that you should use the Deque interface implemented by ArrayDeque.

Deque<Integer> modernStack = new ArrayDeque<>(); modernStack.push(10); modernStack.pop();

Why is ArrayDeque better?

  • It uses a highly optimized Circular Dynamic Array.
  • It carries zero synchronization overhead, making it significantly faster.
  • It strictly enforces Queue/Stack behavior without exposing arbitrary middle-index insertion methods.

7. Real-World Applications of the Stack Architecture

  • Browser Back Button: Navigating backwards drops you into the most recently visited page (LIFO).
  • Undo Functionality: Text editors reverse the most recently executed action first.
  • JVM Method Execution (The Call Stack): When main() calls methodA() which calls methodB(), methodB() executes and pops off the stack first. If recursion goes too deep and runs out of memory, the JVM throws a fatal StackOverflowError.
  • Graph Algorithms: Depth-First Search (DFS) relies heavily on a Stack to track exploration paths.

8. Final Summary Table

Feature Stack Behavior
Principle LIFO (Last In, First Out)
Backend Engine Dynamic Array (via Vector)
Thread Safe? Yes (Method-level synchronization)
Cache Friendly? Yes (Contiguous array memory)
Performance Slower (Due to lock overhead)
Time Complexity push(): $O(1)$ | pop(): $O(1)$ | peek(): $O(1)$
Legacy Class? Yes
Modern Alternative ArrayDeque

Comments

Popular posts from this blog

How I Got Selected in MNC

Virtusa Sometimes success does not come from having the best coding skills or the perfect roadmap. Sometimes it comes from simply refusing to quit. This is the honest story of how I transitioned from a confused, rejected fresher to getting selected as an Associate Engineer at Virtusa. The Beginning: Confused About My Future After completing my graduation, I stared blankly at my career options. Like many freshers, I lacked a clear direction. Should I join a Java course? Should I prepare on my own? Should I just wait for campus placement opportunities? One day, I called my friend Chetan. He suggested I join Naresh i Technologies and start learning Java seriously. Still unsure of my path, I told him I needed time to think about it. A couple of days later, my phone buzzed with a WhatsApp message offering a job opportunity. They asked me to come for the next round of the recruitment process. Excitement completely took over. I packed my bags, traveled to th...

Spring Boot Introduction

Spring Boot Introduction: Architecture, Dependencies, and Embedded Servers Modern enterprise applications demand rapid development, frictionless deployment, and absolute minimal configuration. Before Spring Boot arrived, developers utilizing the Spring Framework wasted immense amounts of time configuring XML files, managing clashing dependencies, setting up clunky application servers, and stitching various Spring modules together manually. To eliminate these bottlenecks, Pivotal introduced Spring Boot . Built entirely on top of the traditional Spring Framework, Spring Boot is an "opinionated" framework. It aggressively simplifies application development by injecting auto-configuration, packaging starter dependencies, and embedding web servers directly into your application. This allows backend developers to focus entirely on building business logic rather than wrestling with infrastructure setup. What is Spring Boot? Spring Boot is a powerful extens...

Strings in C

C Programming: Working with Strings Unlike modern programming languages like Python or Java, C does not possess a dedicated "String" data type. Instead, C treats a string as a simple 1D array of characters. Real-life example: Think of a freight train. The train does not exist as one solid object; it consists of individual boxcars linked together. Similarly, a C string links individual characters side-by-side in memory. To let the computer know the train has ended, C attaches a special "caboose" called the Null Terminator ( \0 ). 1. Essential String Functions Handling strings manually requires complex loops. To save time, C provides a built-in library called <string.h> that contains powerful functions to manipulate text. strlen(): You use this to find the exact length of a string. The compiler counts the characters until it hits the \0 terminator. It does not count the terminator itself. strcpy(): You ...