Lesson 118 min read

Collections Framework

Flexible containers that grow, shrink, and organize

Why Collections?

Arrays are great, but they have a big limitation: fixed size. What if you don't know how many items you'll need? What if you want to quickly look up a value by name instead of by number? That's where the Collections Framework comes in.

Think of it as a toolbox of advanced containers:

  • ArrayList — a resizable array. Like a backpack that magically gets bigger as you add more stuff.
  • LinkedList — a chain of nodes. Great for adding/removing from the beginning or end.
  • HashSet — a bag that doesn't allow duplicates. Like a guest list where each name can only appear once.
  • HashMap — a dictionary built on hash functions. Look up values by key, like finding a phone number by name.

All collections use generics (the <Type> in angle brackets) to specify what type they hold.

ArrayList — Your Go-To List

import java.util.ArrayList;
import java.util.Collections;
public class Main {
public static void main(String[] args) {
ArrayList<String> fruits = new ArrayList<>();
// Add items
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
fruits.add("Banana"); // duplicates are OK!
System.out.println("Fruits: " + fruits);
System.out.println("Size: " + fruits.size());
System.out.println("First: " + fruits.get(0));
// Remove by value
fruits.remove("Banana"); // removes first occurrence
System.out.println("After remove: " + fruits);
// Check if it contains something
System.out.println("Has Cherry? " + fruits.contains("Cherry"));
// Sort alphabetically
fruits.add("Avocado");
Collections.sort(fruits);
System.out.println("Sorted: " + fruits);
// Loop through
System.out.print("Each: ");
for (String f : fruits) {
System.out.print(f + " ");
}
System.out.println();
}
}
Output
Fruits: [Apple, Banana, Cherry, Banana]
Size: 4
First: Apple
After remove: [Apple, Cherry, Banana]
Has Cherry? true
Sorted: [Apple, Avocado, Banana, Cherry]
Each: Apple Avocado Banana Cherry

HashSet & HashMap

A HashSet is perfect when you care about uniqueness. Adding a duplicate? It just ignores it. Think of it like a stamp collection — you don't want two of the same stamp.

A HashMap stores key-value pairs. It's like a real dictionary: the word is the key, the definition is the value. You look things up by key, and it's crazy fast — nearly instant, no matter how many entries you have.

HashSet — No Duplicates Allowed

import java.util.HashSet;
public class Main {
public static void main(String[] args) {
HashSet<String> visitors = new HashSet<>();
visitors.add("Alice");
visitors.add("Bob");
visitors.add("Alice"); // duplicate — ignored!
visitors.add("Charlie");
visitors.add("Bob"); // duplicate — ignored!
System.out.println("Visitors: " + visitors);
System.out.println("Count: " + visitors.size()); // 3, not 5
System.out.println("Has Alice? " + visitors.contains("Alice"));
System.out.println("Has Dave? " + visitors.contains("Dave"));
visitors.remove("Bob");
System.out.println("After removing Bob: " + visitors);
// Great for removing duplicates from a list!
String[] words = {"cat", "dog", "cat", "bird", "dog", "cat"};
HashSet<String> unique = new HashSet<>();
for (String w : words) {
unique.add(w);
}
System.out.println("Unique words: " + unique);
}
}
Output
Visitors: [Alice, Bob, Charlie]
Count: 3
Has Alice? true
Has Dave? false
After removing Bob: [Alice, Charlie]
Unique words: [cat, bird, dog]

HashMap — The Power of Key-Value Pairs

import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, Integer> scores = new HashMap<>();
// Put key-value pairs
scores.put("Alice", 95);
scores.put("Bob", 82);
scores.put("Charlie", 91);
// Get by key
System.out.println("Alice's score: " + scores.get("Alice"));
// Check if key exists
System.out.println("Has Bob? " + scores.containsKey("Bob"));
// Update a value
scores.put("Bob", 88); // same key = replace value
System.out.println("Bob's new score: " + scores.get("Bob"));
// getOrDefault — safe lookup
System.out.println("Dave's score: " + scores.getOrDefault("Dave", 0));
// Loop through all entries
System.out.println("\nAll scores:");
for (String name : scores.keySet()) {
System.out.println(" " + name + ": " + scores.get(name));
}
// Count word frequency — classic HashMap pattern
String sentence = "the cat sat on the mat the cat";
HashMap<String, Integer> freq = new HashMap<>();
for (String word : sentence.split(" ")) {
freq.put(word, freq.getOrDefault(word, 0) + 1);
}
System.out.println("\nWord counts: " + freq);
}
}
Output
Alice's score: 95
Has Bob? true
Bob's new score: 88
Dave's score: 0

All scores:
  Bob: 88
  Alice: 95
  Charlie: 91

Word counts: {the=3, cat=2, sat=1, on=1, mat=1}
Note: Collections can't hold primitives like int or double directly. Instead, Java uses wrapper classes: Integer, Double, Boolean, etc. Java auto-converts between them (called autoboxing), so ArrayList<Integer> works seamlessly with int values.

Quick check

What happens when you add a duplicate element to a HashSet?

Continue reading

Hash FunctionsData Structure
Collisions, load factor, probing
Sets vs MapsData Structure
Use cases and tradeoffs
Dynamic ArraysData Structure
Resizing strategy, amortized O(1) push
Dictionaries & SetsPython
Look things up by name instead of by number
ObjectsJavascript
Key-value pairs — the backbone of JS
Interfaces & Abstract ClassesGenerics