Java Collections Framework Cheat Sheet
Covers List, Set, and Map basics, choosing between implementations, sorting with Comparator, and immutable collection factories.
List, Set, and Map Basics
The three core collection types and their fundamental operations.
List<String> list = new ArrayList<>();list.add("a");list.add("b");list.get(0); // "a" - indexed access, O(1) for ArrayListlist.remove("a");Set<String> set = new HashSet<>();set.add("x");set.add("x"); // ignored - duplicates not allowedSystem.out.println(set.size()); // 1Map<String, Integer> map = new HashMap<>();map.put("age", 30);map.getOrDefault("height", 0); // 0 - safe default lookupmap.forEach((k, v) -> System.out.println(k + "=" + v));
Choosing Implementations
Trade-offs between the common List, Set, and Map implementations.
List<Integer> arrayList = new ArrayList<>(); // fast random access, slow middle insertsList<Integer> linkedList = new LinkedList<>(); // fast insert/remove at ends, slow random accessSet<Integer> hashSet = new HashSet<>(); // O(1) avg, no order guaranteeSet<Integer> linkedHashSet = new LinkedHashSet<>(); // preserves insertion orderSet<Integer> treeSet = new TreeSet<>(); // sorted, O(log n)Map<String, Integer> hashMap = new HashMap<>(); // no order guaranteeMap<String, Integer> linkedHashMap = new LinkedHashMap<>(); // insertion orderMap<String, Integer> treeMap = new TreeMap<>(); // sorted by key
Iteration & Sorting
Sort collections and safely remove items while iterating.
List<String> names = new ArrayList<>(List.of("Charlie", "Alice", "Bob"));Collections.sort(names); // natural ordering (alphabetical)names.sort(Comparator.reverseOrder()); // descendingnames.sort(Comparator.comparing(String::length).thenComparing(Comparator.naturalOrder()));for (String name : names) { // enhanced for-loop (uses Iterator internally) System.out.println(name);}Iterator<String> it = names.iterator();while (it.hasNext()) { if (it.next().equals("Bob")) it.remove(); // safe removal during iteration}
Core Interfaces & Complexity
Which interface to reach for, and its typical performance profile.
- Collection- Root interface for List, Set, and Queue
- List- Ordered, allows duplicates; implementations: ArrayList, LinkedList, Vector
- Set- No duplicates; implementations: HashSet, LinkedHashSet, TreeSet
- Map- Key-value pairs, not a Collection subtype; implementations: HashMap, TreeMap, LinkedHashMap
- Queue / Deque- FIFO/double-ended access; implementations: ArrayDeque, LinkedList, PriorityQueue
- ArrayList get/add(end)- O(1) amortized; add/remove in the middle is O(n)
- HashMap get/put- O(1) average case, O(n) worst case on hash collisions
- TreeMap/TreeSet- O(log n) operations, keeps elements sorted via Comparable or Comparator
Immutable Collections & Utilities
Create read-only collections and use the Collections helper class.
List<String> immutable = List.of("a", "b", "c"); // Java 9+, throws on mutationMap<String, Integer> immutableMap = Map.of("x", 1, "y", 2);List<String> unmodifiable = Collections.unmodifiableList(list); // read-only viewCollections.max(list);Collections.reverse(list);Collections.emptyList();
Streams API over Collections
Transform and aggregate collections declaratively with the Stream pipeline.
List<String> names = List.of("Alice", "Bob", "Charlie", "Ana", "Ben");Map<Character, List<String>> byFirstLetter = names.stream() .collect(Collectors.groupingBy(n -> n.charAt(0)));long countLongNames = names.stream() .filter(n -> n.length() > 3) .count();String joined = names.stream() .sorted() .collect(Collectors.joining(", ", "[", "]"));Map<Boolean, List<String>> partitioned = names.stream() .collect(Collectors.partitioningBy(n -> n.length() <= 3));double avgLength = names.stream() .collect(Collectors.averagingInt(String::length));
Thread-Safe Collections
Reach for java.util.concurrent instead of synchronizing legacy collections manually.
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();concurrentMap.computeIfAbsent("hits", k -> 0);concurrentMap.merge("hits", 1, Integer::sum); // atomic read-modify-writeList<String> cowList = new CopyOnWriteArrayList<>(); // safe iteration, expensive writescowList.add("event"); // copies the backing array on every mutationBlockingQueue<Runnable> tasks = new LinkedBlockingQueue<>();tasks.put(() -> System.out.println("work")); // blocks if bounded and full// Legacy synchronized wrapper - coarser locking than ConcurrentHashMapMap<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
Custom Keys in HashMap/HashSet
Objects used as hash-based keys must implement a correct, consistent equals/hashCode pair.
public final class CacheKey { private final String tenant; private final long id; public CacheKey(String tenant, long id) { this.tenant = tenant; this.id = id; } @Override public boolean equals(Object o) { if (!(o instanceof CacheKey other)) return false; return id == other.id && tenant.equals(other.tenant); } @Override public int hashCode() { return Objects.hash(tenant, id); } // must match equals}Map<CacheKey, Object> cache = new HashMap<>();cache.put(new CacheKey("acme", 1), "value");cache.containsKey(new CacheKey("acme", 1)); // true - different instance, equal key// Mutating a field used in equals/hashCode AFTER insertion corrupts the bucket - avoid mutable keys
PriorityQueue & Deque as Stack/Queue
Use a heap-backed queue for ordered processing and a Deque for both stack and queue semantics.
PriorityQueue<Task> tasks = new PriorityQueue<>(Comparator.comparingInt(Task::priority));tasks.offer(new Task("low", 3));tasks.offer(new Task("urgent", 1));tasks.poll(); // returns "urgent" first - lowest value = highest priority (min-heap)Deque<Integer> stack = new ArrayDeque<>();stack.push(1); stack.push(2);stack.pop(); // 2 - LIFODeque<Integer> queue = new ArrayDeque<>();queue.offer(1); queue.offer(2);queue.poll(); // 1 - FIFO// ArrayDeque is preferred over Stack/LinkedList for both use cases - no legacy sync overhead
Iterator Semantics & Pitfalls
Behavior distinctions that cause subtle bugs in production code.
- Fail-fast iterators- ArrayList/HashMap iterators throw ConcurrentModificationException if the collection is structurally modified outside the iterator during iteration
- Fail-safe iterators- CopyOnWriteArrayList and ConcurrentHashMap iterate over a snapshot/weakly-consistent view and never throw CME
- ListIterator- Supports bidirectional traversal and in-place set()/add() during iteration, unlike a plain Iterator
- removeIf- Collection.removeIf(predicate) safely removes matching elements without manual iterator handling
- Comparator.nullsFirst / nullsLast- Wraps a comparator to define ordering for null elements instead of throwing NullPointerException
- Spliterator- Backing abstraction for streams that supports splitting a source for parallel traversal
- toArray(IntFunction)- list.toArray(String[]::new) avoids the raw toArray() Object[] cast pitfall
Always override both equals() and hashCode() together when using custom objects as HashMap/HashSet keys - inconsistent implementations silently break lookups because the object may hash into the wrong bucket.