Introduction to Stack ADT
In computer science, a Stack is an abstract data type (ADT) that serves as a collection of elements, with two principal operations:
- Push: Adds an item to the top of the stack.
- Pop: Removes the item from the top of the stack.
A Stack follows the LIFO (Last In, First Out) principle. This means the last item inserted will be the first item removed. It is similar to a deck of cards where you can only access the top card.
Why is Stack ADT Important?
The Stack ADT is a fundamental concept in computer science because it forms the basis for many system-level functionalities, including:
- Function call management (Call Stack)
- Undo operations in text editors
- Browser history management
- Recursion handling
The Stack ADT can be implemented using:
- Array-based stack
- Linked list-based stack
Stack Operations
Push Operation
The Push operation is used to insert a new element on the top of the stack.
Example:
- Push(10)
- Push(20)
- Push(30)
Stack now looks like:
30 <- Top 20 10
Pop Operation
The Pop operation removes the topmost element from the stack.
Example:
- Pop() -> Removes 30
- Pop() -> Removes 20
Stack now looks like:
10 <- Top
Peek Operation
The Peek operation returns the topmost element without removing it from the stack.
Example:
- Peek() -> Returns 10
- Stack remains unchanged
Stack Memory vs Heap Memory
What is Stack Memory?
Stack Memory is a region in Random Access Memory (RAM) that stores local variables, method calls, and function call references.
- Fast access: Stack memory is fast because memory is allocated and deallocated automatically.
- Small size: It has a fixed small size, generally limited.
- Stores function calls and local variables.
What is Heap Memory?
Heap Memory is also a region in RAM where dynamically allocated objects are stored.
- Large size: Heap memory is larger than stack memory.
- Slow access: Accessing data from heap is slower than stack.
- Holds objects, arrays, and reference data.
Stack Frame in Stack Memory
What is a Stack Frame?
A Stack Frame is a block of memory in the stack that stores:
- Method parameters
- Local variables
- Return address
Every time a function/method is called, a stack frame is created. When the function ends, the stack frame is automatically destroyed.
Function Call Flow in Stack Memory
Let’s understand how stack memory works with a function call.
Example Code
public class StackExample { public static void main(String[] args) { function1(); } static void function1() { int a = 10; function2(); } static void function2() { int b = 20; function3(); } static void function3() { int c = 30; } }
Stack Memory Flow
- function1() is called → Stack Frame for
function1
is created. - function2() is called → Stack Frame for
function2
is created. - function3() is called → Stack Frame for
function3
is created. - function3() finishes → Stack Frame is removed.
- function2() finishes → Stack Frame is removed.
- function1() finishes → Stack Frame is removed.
Objects in Heap Memory
When we create an object using new
keyword, it is stored in the Heap Memory.
Example Code
public class House { int windows; int doors; public House(int windows, int doors) { this.windows = windows; this.doors = doors; } public static void main(String[] args) { House myHouse = new House(5, 10); } }
Memory Allocation
- Heap Memory: The House object is stored in heap.
- Stack Memory: The reference
myHouse
is stored in stack.
Garbage Collection
What is Garbage Collection?
Garbage Collection (GC) is an automatic process of freeing up memory from unused objects in the heap.
Example
House house1 = new House(5, 10); House house2 = new House(6, 12); house1 = null; // Eligible for Garbage Collection
When house1
reference is set to null, the object is no longer accessible, making it eligible for garbage collection.
Stack Implementation with Linked List
Node.java
public class Node<T extends Comparable<T>> { private T data; private Node<T> nextNode; public Node(T data){ this.data = data; } public T getData() { return data; } public void setData(T data) { this.data = data; } public Node<T> getNextNode() { return nextNode; } public void setNextNode(Node<T> nextNode) { this.nextNode = nextNode; } @Override public String toString() { return this.data.toString(); } }
Stack.java
public class Stack<T extends Comparable<T>> { private Node<T> root; private int count; // O(1) constant time public void push(T newData){ this.count++; if( this.root == null ){ this.root = new Node<>(newData); }else{ Node<T> oldRoot = this.root; this.root = new Node<>(newData); this.root.setNextNode(oldRoot); } } public int size(){ return this.count; } public T pop(){ T itemToPop = this.root.getData(); this.root = this.root.getNextNode(); this.count--; return itemToPop; } public boolean isEmpty(){ return this.root == null; } }
App.java
public class App { public static void main(String[] args) { Stack<Integer> stack = new Stack<>(); stack.push(10); stack.push(20); System.out.println(stack.pop()); System.out.println(stack.size()); System.out.println(stack.pop()); System.out.println(stack.isEmpty()); } }
Stack implementation with Array
Stack.java
public class Stack<Item> { private Item[] stack; private int numberOfItems; public Stack(){ this.stack = ( Item[] ) new Object[1]; } public void push(Item item){ if( numberOfItems == this.stack.length ){ resize(2*this.stack.length); } this.stack[numberOfItems++] = item; } public Item pop(){ Item itemToPop = this.stack[--numberOfItems]; if( numberOfItems > 0 && numberOfItems == this.stack.length/4 ){ resize(this.stack.length/2); } return itemToPop; } public boolean isEmpty(){ return this.numberOfItems == 0; } public int size(){ return this.numberOfItems; } // O(n) private void resize(int capacity) { Item[] stackCopy = ( Item[] ) new Object[capacity]; for(int i=0;i<numberOfItems;i++){ stackCopy[i]=this.stack[i]; } this.stack = stackCopy; } }
App.java
public class App { public static void main(String[] args) { Stack<String> stack = new Stack<>(); stack.push("Roger"); stack.push("James"); System.out.println(stack.size()); System.out.println(stack.pop()); System.out.println(stack.size()); } }
Stack in Java
public class Stack<E> extends Vector<E>
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. The usual push
and pop
operations are provided, as well as a method to peek
at the top item on the stack, a method to test for whether the stack is empty
, and a method to search
the stack for an item and discover how far it is from the top.
When a stack is first created, it contains no items.
A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. For example:
Deque<Integer> stack = new ArrayDeque<Integer>();