On this page
Collections Best Practices
Choosing the right collection type and using it correctly is essential for readable, performant Java code.
Choosing the Right Collection
| Need | Recommended |
|---|---|
| Ordered list with fast random access | ArrayList |
| Frequent insert/delete at ends or middle | LinkedList |
| Unique elements, no order needed | HashSet |
| Unique elements, sorted order | TreeSet |
| Key-value lookup, no order | HashMap |
| Key-value lookup, sorted by key | TreeMap |
| Thread-safe queue | ConcurrentLinkedQueue, ArrayBlockingQueue |
| Fixed-size ordered list | List.of() (immutable) |
| Enum keys/values | EnumMap, EnumSet |
Prefer Interfaces in APIs
// Good — flexible for callers
public void process(List<String> items) { }
// Avoid — locks callers into ArrayList
public void process(ArrayList<String> items) { }
Declare variables and parameters as List, Set, Map, Queue — instantiate with concrete types.
Immutability
Use immutable collections when data should not change:
List<String> fixed = List.of("a", "b", "c");
Set<Integer> ids = Set.of(1, 2, 3);
Map<String, Integer> scores = Map.of("Alice", 95, "Bob", 87);
// Java 9+ copy constructors
List<String> copy = List.copyOf(mutableList);
Immutable collections throw UnsupportedOperationException on modification attempts.
Initial Capacity
Pre-size collections when the expected size is known:
List<String> list = new ArrayList<>(1000);
Map<String, User> map = new HashMap<>(1000);
HashMap default load factor is 0.75 — for 1000 entries, use new HashMap<>(1334).
Iteration
// Enhanced for-loop — preferred for simple iteration
for (String item : list) {
System.out.println(item);
}
// Iterator — safe removal during iteration
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().startsWith("X")) {
it.remove();
}
}
// forEach with lambda
list.forEach(System.out::println);
Do not modify a collection while iterating with enhanced for-loop — use Iterator.remove() or removeIf().
Null Handling
| Collection | Allows null keys/elements? |
|---|---|
ArrayList |
Yes (elements) |
HashMap |
Yes (one null key, null values) |
TreeMap |
No (null keys throw NPE) |
HashSet |
Yes (one null element) |
ConcurrentHashMap |
No |
Be consistent — avoid mixing null and non-null in the same collection.
Performance Tips
- ArrayList — O(1) get/set, O(n) insert/remove in middle
- LinkedList — O(1) insert/remove at ends, O(n) random access
- HashMap — O(1) average get/put; use good
hashCode()/equals() - TreeMap/TreeSet — O(log n) operations; use when sorted order is needed
- Prefer enhanced for-loop or streams over indexed loops for readability
Common Mistakes
// ❌ Modifying list while iterating
for (String s : list) {
if (s.isEmpty()) list.remove(s); // ConcurrentModificationException
}
// ✅ Use removeIf
list.removeIf(String::isEmpty);
// ❌ Using raw types
List list = new ArrayList(); // Avoid
// ✅ Use generics
List<String> list = new ArrayList<>();
Best Practices Summary
- Program to interfaces, not implementations
- Use immutable collections for constants and return values
- Choose the simplest collection that meets your requirements
- Pre-size collections when size is predictable
- Use generics everywhere — never raw types