1. The Hook (The "Byte-Sized" Intro)
In a Nutshell: Choosing the wrong collection can make your code 100x slower! ArrayList beats LinkedList for 99% of use cases. HashMap is your default Map. TreeSet/TreeMap only when you need sorting. Initialize capacity when size is known. Program to interfaces for flexibility. Benchmark when performance matters!
Think of tool selection. Using a hammer for screws (LinkedList for random access) works but is painfully slow. Use the right tool (screwdriver/ArrayList) for the job!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Performance Matrix
Time Complexity Cheat Sheet:
- ArrayList: get=O(1), add=O(1)*, remove=O(n)
- LinkedList: get=O(n), add=O(1), remove=O(1)
- HashSet/HashMap: all operations O(1) average
- TreeSet/TreeMap: all operations O(log n)
*amortized
3. Technical Mastery (The "Deep Dive")
Complete Performance Table
| Collection | get/search | add/put | remove | Ordering |
|---|---|---|---|---|
| ArrayList | O(1) | O(1)* | O(n) | Insertion |
| LinkedList | O(n) | O(1) | O(1) | Insertion |
| HashSet | O(1) | O(1) | O(1) | None |
| LinkedHashSet | O(1) | O(1) | O(1) | Insertion |
| TreeSet | O(log n) | O(log n) | O(log n) | Sorted |
| HashMap | O(1) | O(1) | O(1) | None |
| TreeMap | O(log n) | O(log n) | O(log n) | Sorted |
4. Interactive & Applied Code
java
import java.util.*;
public class BestPracticesDemo {
public static void main(String[] args) {
// ✅ GOOD: Program to interfaces
List<String> list = new ArrayList<>(); // Can swap to LinkedList easily
// ❌ BAD: Tied to implementation
ArrayList<String> specificList = new ArrayList<>();
// ✅ GOOD: Initialize capacity if known
List<String> largeList = new ArrayList<>(10000);
for (int i = 0; i < 10000; i++) {
largeList.add("item" + i); // No resizing!
}
// ❌ BAD: Default capacity (10), resizes multiple times
List<String> badList = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
badList.add("item" + i); // Many array copies!
}
// ✅ GOOD: Use diamond operator
Map<String, List<Integer>> map = new HashMap<>();
// ❌ BAD: Repeating types
Map<String, List<Integer>> verboseMap = new HashMap<String, List<Integer>>();
// ✅ GOOD: Check before operations
if (!list.isEmpty()) {
String first = list.get(0);
}
// ✅ GOOD: Use enhanced for-loop
for (String item : list) {
System.out.println(item);
}
// ❌ BAD: Manual indexing (error-prone)
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// ✅ GOOD: Use Java 8+ streams for transformations
List<String> uppercased = list.stream()
.map(String::toUpperCase)
.toList(); // Java 16+
// ✅ GOOD: Return empty collections, not null
public List<String> getItems() {
return Collections.emptyList(); // or List.of()
// NOT: return null; // ❌ Causes NullPointerException
}
}
// ✅ GOOD: Defensive copies for mutable parameters
public class ShoppingCart {
private final List<String> items;
public ShoppingCart(List<String> items) {
this.items = new ArrayList<>(items); // Copy, don't store reference!
}
public List<String> getItems() {
return new ArrayList<>(items); // Return copy, not original!
}
}
}5. The Comparison & Decision Layer
Decision Tree: Choosing a Collection
graph TD
A{Key-value pairs?}
A -- Yes --> B{Sorted by keys?}
A -- No --> C{Unique elements?}
B -- Yes --> D[TreeMap]
B -- No --> E{Need order?}
E -- Yes --> F[LinkedHashMap]
E -- No --> G[HashMap]
C -- Yes --> H{Sorted?}
C -- No --> I{Random access?}
H -- Yes --> J[TreeSet]
H -- No --> K{Need order?}
K -- Yes --> L[LinkedHashSet]
K -- No --> M[HashSet]
I -- Yes --> N[ArrayList]
I -- No --> O{Frequent insert/delete at ends?}
O -- Yes --> P[ArrayDeque or LinkedList]
O -- No --> N
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "When would you use LinkedList instead of ArrayList?" Answer: Almost never! ArrayList wins in 99% of cases because:
- Modern CPUs love cache locality (ArrayList = contiguous memory)
- ArrayList
add()is O(1) amortized (resizes 50% larger each time) - LinkedList node overhead (3x memory per element)
Only use LinkedList if:
- Implementing a queue (prefer
ArrayDeque) - Very frequent insertions in middle AND you maintain iterator position
Even then, benchmark first!
Pro-Tips:
- Load factor optimization:
java
// Default load factor = 0.75 (resize when 75% full)
Map<String, Integer> map = new HashMap<>(16, 1.0f); // Higher = less memory, more collisions- Bulk operations:
java
list.addAll(otherList); // ✅ Faster than loop
// vs
for (String s : otherList) list.add(s); // ❌ Slower- EnumSet/EnumMap for enums (crazy fast!):
java
Set<DayOfWeek> weekend = EnumSet.of(SATURDAY, SUNDAY); // Bitset internally!