Lesson 13 • Expert
Collections Framework
Master Java's powerful data structure library — the right collection choice can make your program 100x faster.
📚 Before You Start
You should understand:
- Arrays (Lesson 7) — fixed-size data storage
- OOP (Lesson 9) — classes, objects, and interfaces
- Loops (Lesson 5) — iterating through data
What You'll Learn
- ✅ ArrayList — dynamic resizable arrays for ordered data
- ✅ HashMap — key-value pairs for blazing-fast lookups
- ✅ HashSet — unique elements with O(1) contains checks
- ✅ LinkedList, Stack, and Queue for specialized needs
- ✅ Iterating with for-each, iterators, and streams
- ✅ Choosing the right collection (performance guide)
1️⃣ Why Collections Matter
Real-world analogy: Collections are like different types of storage containers. An ArrayList is a numbered filing cabinet — great for ordered access. A HashMap is a dictionary — look up any word instantly. A HashSet is a bag of unique marbles — quickly check "is this marble in the bag?"
Why not just use arrays? Arrays have a fixed size. Collections resize dynamically, offer rich APIs (search, sort, filter), and provide type safety with generics.
2️⃣ ArrayList — Your Go-To List
Use ArrayList when you need ordered data with fast index access. It's the most commonly used collection.
List<String> names = new ArrayList<>();
names.add("Alice"); // add to end
names.add("Bob");
names.add(1, "Charlie"); // insert at index 1
names.get(0); // "Alice" — O(1)
names.set(0, "Alicia"); // replace at index
names.remove("Bob"); // remove by value
names.size(); // current count
names.contains("Charlie"); // true
// Sort and iterate
Collections.sort(names); // alphabetical
for (String name : names) { // for-each
System.out.println(name);
}Performance: O(1) for get/set by index, O(n) for insert/remove in the middle.
import java.util.*;
public class Main {
record Student(String name, int grade) {}
public static void main(String[] args) {
System.out.println("=== ArrayList Operations ===\n");
// 1. Basic operations
List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie"));
System.out.println("1. Initial list: " + String.join(", ", names));
names.add("Diana");
System.out.println(" After add('Diana'): " + String.join(", ", names));
names.add(1, "Eve"); // insert at index 1
System.out.println(" After add(1, 'Eve'): " + String.join(", ", names));
System.out.println(" get(0): " + names.get(0));
System.out.println(" size: " + names.size());
System.out.println(" contains('Bob'): " + names.contains("Bob"));
names.remove("Bob");
System.out.println(" After remove('Bob'): " + String.join(", ", names));
// 2. Sorting
Collections.sort(names);
System.out.println("\n2. Sorted: " + String.join(", ", names));
// 3. Filtering with streams
List<String> longNames = names.stream()
.filter(n -> n.length() > 4)
.toList();
System.out.println("\n3. Names longer than 4 chars: " + String.join(", ", longNames));
// 4. Transform with streams
List<String> upper = names.stream()
.map(String::toUpperCase)
.toList();
System.out.println("4. Uppercase: " + String.join(", ", upper));
// 5. Student grade list, ranked descending
System.out.println("\n5. Student Grade System:");
List<Student> students = new ArrayList<>(List.of(
new Student("Alice", 95),
new Student("Bob", 82),
new Student("Charlie", 91),
new Student("Diana", 78),
new Student("Eve", 88)
));
students.sort(Comparator.comparingInt(Student::grade).reversed());
System.out.println(" Ranked by grade:");
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println(" " + (i + 1) + ". " + s.name() + ": " + s.grade());
}
double avg = students.stream().mapToInt(Student::grade).average().orElse(0);
System.out.printf(" Average: %.1f%n", avg);
System.out.println(" Top student: " + students.get(0).name());
}
}=== ArrayList Operations ===
1. Initial list: Alice, Bob, Charlie
After add('Diana'): Alice, Bob, Charlie, Diana
After add(1, 'Eve'): Alice, Eve, Bob, Charlie, Diana
get(0): Alice
size: 5
contains('Bob'): true
After remove('Bob'): Alice, Eve, Charlie, Diana
2. Sorted: Alice, Charlie, Diana, Eve
3. Names longer than 4 chars: Alice, Charlie
4. Uppercase: ALICE, CHARLIE, DIANA, EVE
5. Student Grade System:
Ranked by grade:
1. Alice: 95
2. Charlie: 91
3. Eve: 88
4. Bob: 82
5. Diana: 78
Average: 86.8
Top student: Alice3️⃣ HashMap — Key-Value Lookups
Analogy: Like a phone book — you look up a person's name (key) to find their number (value). You don't search page by page; you jump directly to the right entry.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.get("Alice"); // 95 — O(1) average!
scores.containsKey("Bob"); // true
scores.getOrDefault("Diana", 0); // 0 (safe default)
// Common pattern: counting frequency
Map<String, Integer> freq = new HashMap<>();
for (String word : words) {
freq.put(word, freq.getOrDefault(word, 0) + 1);
}4️⃣ HashSet — Unique Elements
Use HashSet when you need to track unique items. Duplicates are silently ignored.
Set<String> tags = new HashSet<>();
tags.add("java");
tags.add("oop");
tags.add("java"); // duplicate — silently ignored!
tags.size(); // 2
tags.contains("java"); // true — O(1)!
// Great for deduplication
List<String> withDupes = Arrays.asList("a", "b", "a", "c", "b");
Set<String> unique = new HashSet<>(withDupes); // {"a", "b", "c"}import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
System.out.println("=== HashMap & HashSet ===\n");
// 1. HashMap — scores (LinkedHashMap keeps insertion order for printing)
System.out.println("1. HashMap (Student Scores):");
Map<String, Integer> scores = new LinkedHashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.put("Charlie", 92);
scores.put("Diana", 88);
System.out.println(" Alice: " + scores.get("Alice"));
System.out.println(" Size: " + scores.size());
System.out.println(" Has 'Bob': " + scores.containsKey("Bob"));
System.out.println(" Unknown: " + scores.getOrDefault("Unknown", -1));
// 2. Word frequency counter (merge is the idiomatic counting pattern)
System.out.println("\n2. Word Frequency Counter:");
String text = "java is great java is powerful java collections are useful collections are great";
Map<String, Integer> freq = new HashMap<>();
for (String w : text.split(" ")) {
freq.merge(w, 1, Integer::sum);
}
freq.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.forEach(e -> System.out.println(" " + e.getKey() + ": " + e.getValue()));
// 3. HashSet — unique tags
System.out.println("\n3. HashSet (Unique Tags):");
Set<String> tags = new LinkedHashSet<>();
tags.add("java"); tags.add("oop"); tags.add("java"); tags.add("collections");
System.out.println(" Tags: " + String.join(", ", tags));
System.out.println(" Size: " + tags.size() + " (duplicate 'java' was ignored!)");
// 4. Deduplication
System.out.println("\n4. Deduplication:");
List<String> dupes = List.of("apple", "banana", "apple", "cherry", "banana", "date");
List<String> unique = new ArrayList<>(new LinkedHashSet<>(dupes));
System.out.println(" Before: " + String.join(", ", dupes));
System.out.println(" After: " + String.join(", ", unique));
// 5. Group students by grade
System.out.println("\n5. Group By Grade:");
record Student(String name, String grade) {}
List<Student> students = List.of(
new Student("Alice", "A"), new Student("Bob", "B"),
new Student("Charlie", "A"), new Student("Diana", "B"),
new Student("Eve", "A")
);
Map<String, List<String>> grouped = students.stream()
.collect(Collectors.groupingBy(Student::grade, TreeMap::new,
Collectors.mapping(Student::name, Collectors.toList())));
grouped.forEach((grade, names) ->
System.out.println(" Grade " + grade + ": " + String.join(", ", names)));
}
}=== HashMap & HashSet ===
1. HashMap (Student Scores):
Alice: 95
Size: 4
Has 'Bob': true
Unknown: -1
2. Word Frequency Counter:
java: 3
is: 2
are: 2
great: 2
collections: 2
powerful: 1
useful: 1
3. HashSet (Unique Tags):
Tags: java, oop, collections
Size: 3 (duplicate 'java' was ignored!)
4. Deduplication:
Before: apple, banana, apple, cherry, banana, date
After: apple, banana, cherry, date
5. Group By Grade:
Grade A: Alice, Charlie, Eve
Grade B: Bob, Dianais/are/great/collections) may print in a different order, since ties aren't ordered.5️⃣ Which Collection Should I Use?
| Need | Use | Why |
|---|---|---|
| Ordered list, read by index | ArrayList | O(1) get, auto-resize |
| Frequent insert/remove at ends | LinkedList | O(1) add/remove at head/tail |
| Key → value mapping | HashMap | O(1) lookup by key |
| Sorted key → value | TreeMap | Keys always sorted |
| Unique elements only | HashSet | O(1) contains check |
| LIFO (undo, backtracking) | Deque/Stack | push/pop O(1) |
| FIFO (task queue, BFS) | Queue/LinkedList | offer/poll O(1) |
Default choice: Start with ArrayList for lists and HashMap for key-value pairs.
6️⃣ Common Beginner Mistakes
❌ Mistake 1: Modifying a collection while iterating
// CRASHES with ConcurrentModificationException!
for (String s : list) {
if (s.equals("remove")) list.remove(s);
}
// CORRECT: use removeIf (Java 8+)
list.removeIf(s -> s.equals("remove"));❌ Mistake 2: Using raw types (no generics)
List list = new ArrayList(); // BAD — no type safety List<String> list = new ArrayList<>(); // GOOD
❌ Mistake 3: Not overriding hashCode() with HashMap keys
If you override equals(), you must also override hashCode().
import java.util.*;
public class Main {
record Task(String name, int priority) {}
public static void main(String[] args) {
System.out.println("=== Stack & Queue ===\n");
// 1. Stack (LIFO) via Deque — preferred over the legacy Stack class
System.out.println("1. Browser History Stack:");
Deque<String> history = new ArrayDeque<>();
history.push("google.com");
history.push("github.com");
history.push("stackoverflow.com");
System.out.println(" Current page: " + history.peek());
System.out.println(" Back (pop): " + history.pop());
System.out.println(" Now on: " + history.peek());
// 2. Undo stack
System.out.println("\n2. Text Editor Undo:");
Deque<String> undoStack = new ArrayDeque<>();
StringBuilder text = new StringBuilder();
for (String chars : new String[]{"Hello", " World", "!"}) {
undoStack.push(text.toString());
text.append(chars);
System.out.println(" Type '" + chars + "' -> '" + text + "'");
}
for (int i = 0; i < 2; i++) {
text = new StringBuilder(undoStack.pop());
System.out.println(" Undo -> '" + text + "'");
}
System.out.println(" Final: '" + text + "'");
// 3. Queue (FIFO)
System.out.println("\n3. Print Queue:");
Queue<String> printQueue = new LinkedList<>();
printQueue.offer("Report.pdf");
printQueue.offer("Photo.jpg");
printQueue.offer("Resume.docx");
System.out.println(" Queue: " + String.join(", ", printQueue));
while (!printQueue.isEmpty()) {
System.out.println(" Printing: " + printQueue.poll());
}
// 4. Practical: priority-ordered task scheduler
System.out.println("\n4. Task Scheduler:");
PriorityQueue<Task> tasks = new PriorityQueue<>(Comparator.comparingInt(Task::priority));
tasks.offer(new Task("Build UI", 2));
tasks.offer(new Task("Write tests", 1));
tasks.offer(new Task("Deploy", 3));
System.out.println(" Tasks (by priority):");
int completed = 0;
while (!tasks.isEmpty()) {
Task task = tasks.poll();
System.out.println(" - " + task.name() + " (priority " + task.priority() + ")");
completed++;
}
System.out.println(" Completed: " + completed + " tasks");
}
}=== Stack & Queue ===
1. Browser History Stack:
Current page: stackoverflow.com
Back (pop): stackoverflow.com
Now on: github.com
2. Text Editor Undo:
Type 'Hello' -> 'Hello'
Type ' World' -> 'Hello World'
Type '!' -> 'Hello World!'
Undo -> 'Hello World'
Undo -> 'Hello'
Final: 'Hello'
3. Print Queue:
Queue: Report.pdf, Photo.jpg, Resume.docx
Printing: Report.pdf
Printing: Photo.jpg
Printing: Resume.docx
4. Task Scheduler:
Tasks (by priority):
- Write tests (priority 1)
- Build UI (priority 2)
- Deploy (priority 3)
Completed: 3 tasks📋 Quick Reference
| Collection | Key Methods | Complexity |
|---|---|---|
| ArrayList | add, get, set, remove, size | O(1) get, O(n) insert |
| LinkedList | addFirst, addLast, poll | O(1) ends, O(n) middle |
| HashMap | put, get, containsKey, remove | O(1) average |
| TreeMap | put, get, firstKey, lastKey | O(log n) sorted |
| HashSet | add, contains, remove | O(1) average |
| Stack/Deque | push, pop, peek | O(1) |
🎉 Lesson Complete!
You now know how to use Java's core collections — ArrayList, HashMap, HashSet, Stack, and Queue — and how to choose the right one based on your data access patterns.
Next up: Generics — write type-safe, reusable code that works with any data type.
Sign up for free to track which lessons you've completed and get learning reminders.