Java Collections Framework
The Java collections framework has 25+ years of history. The core types are stable; the modern additions (immutable factories, sequenced collections) round out long-standing gaps. This page is the practical guide — which collection to pick when, the immutable variants, and the patterns that have aged well.
The core types
List
Ordered, allows duplicates, indexed access.
| Implementation | When to use |
|----------------|-------------|
| `ArrayList` | Default. Backed by array; fast random access; slow inserts in middle |
| `LinkedList` | Almost never. Doubly-linked list; rarely the right answer |
| `CopyOnWriteArrayList` | Read-heavy concurrent access; writes copy the entire array |
Default to `ArrayList`. The cases for the others are narrow.
Set
Unordered (or ordered), no duplicates.
| Implementation | When to use |
|----------------|-------------|
| `HashSet` | Default. Hash-based, no order |
| `LinkedHashSet` | Insertion-order iteration matters |
| `TreeSet` | Natural-order or custom-order iteration |
| `ConcurrentSkipListSet` | Concurrent ordered set |
| `EnumSet` | Set of enum values; bit-vector backed; very fast |
`HashSet` is the right default for most cases. `LinkedHashSet` if you need to preserve insertion order.
Map
Key-value pairs, unique keys.
| Implementation | When to use |
|----------------|-------------|
| `HashMap` | Default. Fast; no ordering |
| `LinkedHashMap` | Insertion-order or access-order matters |
| `TreeMap` | Sorted by key |
| `ConcurrentHashMap` | Concurrent map (the right concurrent map) |
| `EnumMap` | Map keyed by enum; very fast |
`HashMap` for general use. `ConcurrentHashMap` for shared mutable maps across threads. `EnumMap` when keyed by enum (faster, less memory).
Queue / Deque
| Implementation | When to use |
|----------------|-------------|
| `ArrayDeque` | Default for stack/queue use; faster than LinkedList |
| `PriorityQueue` | Heap-based priority queue |
| `LinkedBlockingQueue` | Producer-consumer with blocking semantics |
| `ConcurrentLinkedQueue` | Concurrent queue, non-blocking |
`ArrayDeque` for non-concurrent stack/queue. The `Stack` class is legacy; do not use it.
Immutable factories (Java 9+)
```java
List<String> names = List.of("Alice", "Bob", "Carol");
Set<Integer> ports = Set.of(80, 443, 8080);
Map<String, String> headers = Map.of("Content-Type", "application/json");
```
The result is genuinely immutable — modification throws `UnsupportedOperationException`. These are the right default for "I have a small fixed collection."
For larger or computed immutable collections:
```java
List<X> result = stream.collect(Collectors.toUnmodifiableList());
// or in Java 16+
List<X> result = stream.toList();
```
Sequenced collections (Java 21+)
Long-standing API gaps filled. `SequencedCollection`, `SequencedSet`, `SequencedMap` interfaces add:
- `getFirst()`, `getLast()`
- `addFirst(e)`, `addLast(e)`
- `reversed()` — returns a reversed view
- `removeFirst()`, `removeLast()`
Implemented by `List`, `LinkedHashSet`, `LinkedHashMap`, `Deque`, etc. Removes the need for awkward `iterator().next()` patterns to access the first element.
Iteration patterns
Enhanced for loop (default)
```java
for (String name : names) {
process(name);
}
```
Almost always the right way to iterate when you don't need the index.
Iterator (when you need to remove during iteration)
```java
Iterator<String> it = names.iterator();
while (it.hasNext()) {
if (shouldRemove(it.next())) {
it.remove();
}
}
```
Index-based (when you need the index)
```java
for (int i = 0; i < names.size(); i++) {
System.out.println(i + ": " + names.get(i));
}
```
Streams (for transformation/aggregation)
```java
names.stream()
.filter(n -> n.length() > 5)
.forEach(this::process);
```
See [JavaStreamsAndFunctionalProgramming](JavaStreamsAndFunctionalProgramming).
Common operations and their costs
| Operation | ArrayList | LinkedList | HashMap | TreeMap |
|-----------|-----------|------------|---------|---------|
| Get by index | O(1) | O(n) | n/a | n/a |
| Get by key | n/a | n/a | O(1) avg | O(log n) |
| Insert at end | O(1) amortized | O(1) | O(1) avg | O(log n) |
| Insert at front | O(n) | O(1) | n/a | n/a |
| Remove by value | O(n) | O(n) | O(1) avg | O(log n) |
| Iteration | O(n) | O(n) | O(n) | O(n) ordered |
The "amortized" on ArrayList insert: occasionally an internal array resize is O(n), averaged out across many inserts.
Memory and capacity
- `ArrayList(int initialCapacity)` — provide expected size if known to avoid resizing
- `HashMap(int initialCapacity)` — initial bucket count; avoid rehashing
- `HashMap(int capacity, float loadFactor)` — load factor 0.75 is the default
Pre-sizing collections has a real performance impact when sizes are known and large.
Thread safety
Default collections are not thread-safe. Three options for concurrent access:
1. **Concurrent collections** (`ConcurrentHashMap`, `CopyOnWriteArrayList`, `ConcurrentLinkedQueue`) — designed for concurrent access
2. **Synchronized wrappers** (`Collections.synchronizedXxx`) — single-lock approach; simple but contention can be high
3. **Immutable collections** — no synchronization needed; readers see a consistent snapshot
For maps shared across threads, `ConcurrentHashMap` is almost always the right choice.
Common failure patterns
- **Using `LinkedList` because "it's faster for inserts."** It isn't, in practice. Use `ArrayList`.
- **Synchronizing every collection access.** Often unnecessary if the collection is single-threaded; expensive if concurrent collections would work better.
- **Mutating collections during iteration.** ConcurrentModificationException. Use `Iterator.remove()` or copy.
- **`HashMap` resize thrashing.** Pre-size if you know the count.
- **Returning mutable collections from APIs.** Caller can break invariants. Return immutable copies.
- **Using `Vector` or `Hashtable`.** Legacy, synchronized for everything. Use modern equivalents.
Further Reading
- [JavaStreamsAndFunctionalProgramming](JavaStreamsAndFunctionalProgramming) — Streams operate on collections
- [JavaTwentyOneFeatures](JavaTwentyOneFeatures) — Sequenced collections and other additions
- [ImmutableDataPatterns](ImmutableDataPatterns) — Immutable collection design
- [JavaRecordsAndSealedClasses](JavaRecordsAndSealedClasses) — Records often flow through collections
- [Java Hub](JavaHub) — Cluster index