1. The Hook (The "Byte-Sized" Intro)
In a Nutshell: Java 9+ factory methods (List.of(), Set.of(), Map.of()) create truly immutable collections—cannot be modified after creation. Unlike Collections.unmodifiableXxx() (which are views), these are compact, null-rejecting, and thread-safe by design. Use them for constants, configuration, and defensive programming.
Think of museum exhibits behind glass. You can view artifacts (read) but cannot touch or modify them. That's immutable collections!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy: Frozen Snapshot
- Mutable List: Whiteboard (erase, add)
- unmodifiableList(): Laminated copy of whiteboard (original can still change!)
- List.of(): Carved in stone (truly permanent)
3. Technical Mastery (The "Deep Dive")
Factory Methods (Java 9+)
| Method | Example | Max Elements |
|---|---|---|
| List.of() | List.of(1, 2, 3) | Unlimited (varargs) |
| Set.of() | Set.of("A", "B") | Unlimited |
| Map.of() | Map.of("k1", "v1", "k2", "v2") | 10 (key-value pairs) |
| Map.ofEntries() | Map.ofEntries(entry("k1", "v1")) | Unlimited |
Characteristics
- Truly immutable: Cannot change structure OR elements
- Null-hostile: Throws
NullPointerExceptionif null - Compact: Space-efficient implementations
- Thread-safe: No synchronization needed
4. Interactive & Applied Code
java
import java.util.*;
import static java.util.Map.entry; // Java 9+
public class ImmutableDemo {
public static void main(String[] args) {
// LIST.OF()
List<String> immutableList = List.of("Apple", "Banana", "Cherry");
System.out.println("List: " + immutableList);
// immutableList.add("Date"); // ❌ UnsupportedOperationException
// immutableList.set(0, "Apricot"); // ❌ UnsupportedOperationException
// SET.OF()
Set<Integer> immutableSet = Set.of(1, 2, 3);
System.out.println("Set: " + immutableSet);
// immutableSet.add(4); // ❌ UnsupportedOperationException
// Set.of(1, 2, 2); // ❌ IllegalArgumentException (duplicates!)
// MAP.OF() (up to 10 key-value pairs)
Map<String, Integer> smallMap = Map.of(
"Alice", 25,
"Bob", 30,
"Charlie", 35
);
System.out.println("Map: " + smallMap);
// MAP.OFENTRIES() (unlimited)
Map<String, Integer> largeMap = Map.ofEntries(
entry("key1", 1),
entry("key2", 2),
entry("key3", 3)
// Can have many entries...
);
System.out.println("Large map: " + largeMap);
// NULL REJECTION
try {
List<String> nullList = List.of("A", null, "B");
} catch (NullPointerException e) {
System.out.println("❌ Nulls not allowed: " + e.getClass().getSimpleName());
}
// COMPARISON: unmodifiable vs immutable
List<String> mutable = new ArrayList<>(Arrays.asList("A", "B"));
List<String> unmod = Collections.unmodifiableList(mutable);
mutable.add("C"); // ✅ Original can change
System.out.println("Unmodifiable reflects change: " + unmod); // [A, B, C]
List<String> immutable = List.of("A", "B");
// Cannot change immutable at all!
System.out.println("Immutable: " + immutable); // [A, B] (never changes)
}
}5. The Comparison & Decision Layer
| Feature | Collections.unmodifiableXxx() | List.of() / Set.of() / Map.of() |
|---|---|---|
| Truly immutable | ❌ (view of original) | ✅ (independent copy) |
| Null allowed | ✅ (if original has null) | ❌ (throws NPE) |
| Space efficient | ❌ (wrapper overhead) | ✅ (compact impl) |
| Java Version | 1.2+ | 9+ |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "What's the difference between Collections.unmodifiableList() and List.of()?" Answer:
unmodifiableList(): View of original—if original changes, the "unmodifiable" view changes too!List.of(): Truly immutable—creates independent collection that can NEVER change
java
List<String> original = new ArrayList<>(Arrays.asList("A"));
List<String> unmod = Collections.unmodifiableList(original);
original.add("B"); // unmod now shows [A, B]!
List<String> immutable = List.of("A");
// No way to modify immutable!Pro-Tip: Use Set.copyOf() to create immutable copy:
java
Set<String> mutable = new HashSet<>(Arrays.asList("A", "B"));
Set<String> immutable = Set.copyOf(mutable); // Java 10+
mutable.add("C"); // ✅ Original changes
System.out.println(immutable); // [A, B] (unchanged!)copyOf() creates immutable snapshot!