Your text editor needs undo - the most recent action should be undone first. That's a stack (LIFO). Your print queue processes jobs in order - first submitted, first printed. That's a queue (FIFO).

Undo with a stack

Push actions and pop to undo.

StackUndo.java
import java.util.ArrayDeque;
import java.util.Deque;

public class StackUndo {
    public static void main(String[] args) {
        // Undo history using a stack
        Deque<String> undoHistory = new ArrayDeque<>();
        String currentText = "";
        
        System.out.println("=== Text Editor with Undo ===\n");
        
        // Perform actions (each saves state to undo stack)
        currentText = performAction(undoHistory, currentText, "Hello");
        currentText = performAction(undoHistory, currentText, " World");
        currentText = performAction(undoHistory, currentText, "!");
        
        // Perform more actions
        currentText = performAction(undoHistory, currentText, " How");
        currentText = performAction(undoHistory, currentText, " are");
        currentText = performAction(undoHistory, currentText, " you?");
        
        System.out.println("Current text: \"" + currentText + "\"");
        System.out.println("Undo stack size: " + undoHistory.size());
        
        // Undo operations
        System.out.println("\n=== Performing Undo ===");
        
        currentText = undo(undoHistory, currentText);
        currentText = undo(undoHistory, currentText);
        
        System.out.println("\nAfter 2 undos: \"" + currentText + "\"");
        
        // Undo more actions
        currentText = undo(undoHistory, currentText);
        currentText = undo(undoHistory, currentText);
        System.out.println("After more undos: \"" + currentText + "\"");
    }
    
    static String performAction(Deque<String> history, String current, String addition) {
        history.push(current);  // Save current state
        String newText = current + addition;
        System.out.println("Action: Add \"" + addition + "\" → \"" + newText + "\"");
        return newText;
    }
    
    static String undo(Deque<String> history, String current) {
        if (!history.isEmpty()) {
            String previous = history.pop();  // Get previous state
            System.out.println("Undo: \"" + current + "\" → \"" + previous + "\"");
            return previous;
        } else {
            System.out.println("Nothing to undo!");
            return current;
        }
    }
}

push() adds to top, pop() removes from top. Last in, first out.

stack LIFO collection: last in, first out. Use `Deque` interface in modern Java.

Check before popping

Avoid errors by checking if stack is empty.

StackEmpty.java
import java.util.ArrayDeque;
import java.util.Deque;

public class StackEmpty {
    public static void main(String[] args) {
        Deque<String> browserHistory = new ArrayDeque<>();
        
        System.out.println("=== Browser History (Back Button) ===\n");
        
        // Visit some pages
        visitPage(browserHistory, "google.com");
        visitPage(browserHistory, "github.com");
        visitPage(browserHistory, "stackoverflow.com");
        
        System.out.println("History stack: " + browserHistory);
        System.out.println("Stack size: " + browserHistory.size());
        
        // Go back (pop pages)
        System.out.println("\n=== Going Back ===");
        
        goBack(browserHistory);
        goBack(browserHistory);
        goBack(browserHistory);
        goBack(browserHistory);  // Try to go back when empty!
        
        // Empty stack operations
        System.out.println("\n=== Empty Stack Demo ===");
        Deque<Integer> emptyStack = new ArrayDeque<>();
        
        System.out.println("Stack empty? " + emptyStack.isEmpty());
        System.out.println("Size: " + emptyStack.size());
        
        // Safe methods that return null instead of throwing
        System.out.println("pollFirst() on empty: " + emptyStack.pollFirst());
        System.out.println("peekFirst() on empty: " + emptyStack.peekFirst());
        
        // Dangerous methods (would throw NoSuchElementException)
        // emptyStack.pop();     // Throws!
        // emptyStack.getFirst(); // Throws!
    }
    
    static void visitPage(Deque<String> history, String url) {
        history.push(url);
        System.out.println("Visiting: " + url);
    }
    
    static void goBack(Deque<String> history) {
        if (history.isEmpty()) {
            System.out.println("Can't go back - no history!");
        } else {
            String page = history.pop();
            System.out.println("Back from: " + page);
            
            if (history.isEmpty()) {
                System.out.println("  (Reached start of history)");
            } else {
                System.out.println("  Now at: " + history.peek());
            }
        }
    }
}

Always check isEmpty() before pop() to avoid exceptions.

Process tasks in order

Use a queue for first-come, first-served processing.

QueueTasks.java
import java.util.LinkedList;
import java.util.Queue;

public class QueueTasks {
    public static void main(String[] args) {
        // Task queue - process in order received
        Queue<String> taskQueue = new LinkedList<>();
        
        System.out.println("=== Task Queue System ===\n");
        
        // Add tasks to queue
        System.out.println("Adding tasks:");
        taskQueue.offer("Process order #101");
        System.out.println("  Added: Process order #101");
        
        taskQueue.offer("Send confirmation email");
        System.out.println("  Added: Send confirmation email");
        
        taskQueue.offer("Update inventory");
        System.out.println("  Added: Update inventory");
        
        // Add more tasks
        taskQueue.offer("Generate report");
        taskQueue.offer("Notify warehouse");
        taskQueue.offer("Archive order");
        System.out.println("  Added 3 more tasks...");
        
        System.out.println("\nQueue: " + taskQueue);
        System.out.println("Tasks pending: " + taskQueue.size());
        
        // Process tasks in order
        System.out.println("\n=== Processing Tasks ===");
        
        int taskNum = 1;
        while (!taskQueue.isEmpty()) {
            String task = taskQueue.poll();  // Remove from front
            System.out.println(taskNum + ". " + task + " ✓");
            taskNum++;
        }
        
        System.out.println("\n=== All tasks completed! ===");
        System.out.println("Queue empty: " + taskQueue.isEmpty());
    }
}

offer() adds to back, poll() removes from front. First in, first out.

queue FIFO collection: first in, first out. Use `Queue` interface.

Peek without removing

Look at the top/front element without removing it.

PeekOperations.java
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;

public class PeekOperations {
    public static void main(String[] args) {
        // Stack peek
        Deque<String> stack = new ArrayDeque<>();
        stack.push("Bottom");
        stack.push("Middle");
        stack.push("Top");
        
        System.out.println("=== Stack Peek ===");
        System.out.println("Stack: " + stack);
        System.out.println("peek(): " + stack.peek());  // Look at top
        System.out.println("Stack after peek: " + stack);
        System.out.println("(Unchanged - peek doesn't remove!)");
        
        // Queue peek
        Queue<String> queue = new LinkedList<>();
        queue.offer("First");
        queue.offer("Second");
        queue.offer("Third");
        
        System.out.println("\n=== Queue Peek ===");
        System.out.println("Queue: " + queue);
        System.out.println("peek(): " + queue.peek());  // Look at front
        System.out.println("Queue after peek: " + queue);
        System.out.println("(Unchanged - peek doesn't remove!)");
        
        // Practical: Preview before processing
        System.out.println("\n=== Practical Use: Preview Next Item ===");
        
        Queue<String> printQueue = new LinkedList<>();
        printQueue.offer("Document1.pdf (5 pages)");
        printQueue.offer("Photo.jpg (1 page)");
        printQueue.offer("Report.docx (20 pages)");
        
        System.out.println("Print queue has " + printQueue.size() + " jobs");
        System.out.println("Next up: " + printQueue.peek());
        
        // Process with confirmation
        System.out.println("\n--- Processing ---");
        while (!printQueue.isEmpty()) {
            String next = printQueue.peek();  // Preview
            System.out.println("Printing: " + next);
            printQueue.poll();  // Now remove
        }
        
        // Compare peek vs poll
        System.out.println("\n=== peek() vs poll() ===");
        Queue<Integer> nums = new LinkedList<>();
        nums.offer(1);
        nums.offer(2);
        nums.offer(3);
        
        System.out.println("Queue: " + nums);
        System.out.println("peek() returns: " + nums.peek() + ", queue now: " + nums);
        System.out.println("poll() returns: " + nums.poll() + ", queue now: " + nums);
        System.out.println("poll() returns: " + nums.poll() + ", queue now: " + nums);
    }
}

peek() returns the element without removing it. Useful for inspection.

peek Look at top (stack) or front (queue) without removing.

Reverse a list with stack

Use a stack to reverse element order.

ReverseList.java
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;

public class ReverseList {
    public static void main(String[] args) {
        // Reverse a list using a stack
        List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        
        System.out.println("=== Reversing a List ===");
        System.out.println("Original: " + numbers);
        
        // Push all elements onto stack
        Deque<Integer> stack = new ArrayDeque<>();
        for (int num : numbers) {
            stack.push(num);
        }
        
        System.out.println("Stack (top first): " + stack);
        
        // Pop all elements back into list
        List<Integer> reversed = new ArrayList<>();
        while (!stack.isEmpty()) {
            reversed.add(stack.pop());
        }
        
        System.out.println("Reversed: " + reversed);
        
        // Reverse a string using stack
        System.out.println("\n=== Reversing a String ===");
        String original = "Hello World";
        
        Deque<Character> charStack = new ArrayDeque<>();
        for (char c : original.toCharArray()) {
            charStack.push(c);
        }
        
        StringBuilder reversedString = new StringBuilder();
        while (!charStack.isEmpty()) {
            reversedString.append(charStack.pop());
        }
        
        System.out.println("Original: \"" + original + "\"");
        System.out.println("Reversed: \"" + reversedString + "\"");
        
        // Palindrome check using stack
        System.out.println("\n=== Palindrome Check ===");
        checkPalindrome("radar");
        checkPalindrome("hello");
        checkPalindrome("A man a plan a canal Panama");
    }
    
    static void checkPalindrome(String input) {
        // Clean string: lowercase, letters only
        String clean = input.toLowerCase().replaceAll("[^a-z]", "");
        
        Deque<Character> stack = new ArrayDeque<>();
        for (char c : clean.toCharArray()) {
            stack.push(c);
        }
        
        StringBuilder reversed = new StringBuilder();
        while (!stack.isEmpty()) {
            reversed.append(stack.pop());
        }
        
        boolean isPalindrome = clean.equals(reversed.toString());
        System.out.println("\"" + input + "\" → " + 
            (isPalindrome ? "Palindrome ✓" : "Not palindrome"));
    }
}

Push all items, then pop them - they come out in reverse order.

Exercise: PriorityQueue.java

Explore PriorityQueue: elements ordered by priority, not arrival