Lesson Completion
Back to course

Performance and Best Practices: Choosing the Right Collection

Intermediate
20 minutes4.6Java

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

Collectionget/searchadd/putremoveOrdering
ArrayListO(1)O(1)*O(n)Insertion
LinkedListO(n)O(1)O(1)Insertion
HashSetO(1)O(1)O(1)None
LinkedHashSetO(1)O(1)O(1)Insertion
TreeSetO(log n)O(log n)O(log n)Sorted
HashMapO(1)O(1)O(1)None
TreeMapO(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:

  1. 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
  1. Bulk operations:
java
list.addAll(otherList); // ✅ Faster than loop // vs for (String s : otherList) list.add(s); // ❌ Slower
  1. EnumSet/EnumMap for enums (crazy fast!):
java
Set<DayOfWeek> weekend = EnumSet.of(SATURDAY, SUNDAY); // Bitset internally!

Topics Covered

Java FundamentalsData Structures

Tags

#java#collections#list#set#map#arraylist#hashmap

Last Updated

2025-02-01