Arrays & Collections
Stack and Queue
LIFO and FIFO
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.
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.
Check before popping
Avoid errors by checking if stack is empty.
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.
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.
Peek without removing
Look at the top/front element without removing it.
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.
Reverse a list with stack
Use a stack to reverse element order.
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