Collections framework: choosing the right one
The core interfaces:
List: ordered, allows duplicates. Default choice:ArrayList.LinkedListis rarely better.Set: no duplicates.HashSet(fast, no order),LinkedHashSet(insertion order),TreeSet(sorted).Map: key to value.HashMap,LinkedHashMap,TreeMap, andConcurrentHashMapwhen several threads share it.QueueandDeque:ArrayDequefor stacks and queues,PriorityQueuefor "smallest first".
Java 9 added immutable factories: List.of, Set.of and Map.of. Java 21 added sequenced collections: getFirst(), getLast() and reversed() on lists, deques and ordered sets and maps.
The collections hierarchy
Iterable → Collection → List, Set and Queue/Deque. Map is separate because it holds key-value pairs. Program against the interfaces (List<String> names = new ArrayList<>()) so you can change the implementation later.
List, Set, Queue and Map at a glance
- List: ordered, allows duplicates, access by index (
ArrayList,LinkedList). - Set: no duplicates (
HashSet,LinkedHashSet,TreeSet). - Queue/Deque: process in order, FIFO or LIFO (
ArrayDeque,PriorityQueue). - Map: keys to values, unique keys (
HashMap,LinkedHashMap,TreeMap).
Choosing the right collection
- Need order and index access →
ArrayList - Need to check "seen before?" quickly →
HashSet - Need unique items in insertion order →
LinkedHashSet - Need sorted items or range queries →
TreeSet/TreeMap - Need a stack or queue →
ArrayDeque - Need "smallest next" →
PriorityQueue - Need lookups by key →
HashMap - Shared between threads →
ConcurrentHashMap,CopyOnWriteArrayList
Iterators and fail-fast behaviour
An Iterator walks a collection with hasNext and next, and can remove the current element safely. Changing the collection any other way during iteration throws ConcurrentModificationException (fail-fast). removeIf is the simplest safe removal.
Iterator<Order> it = orders.iterator();
while (it.hasNext()) {
if (it.next().isCancelled()) it.remove(); // safe
}
orders.removeIf(Order::isCancelled); // simplerImmutable and unmodifiable collections
List.of, Set.of and Map.of (Java 9) create unmodifiable collections that reject null. List.copyOf makes an unmodifiable copy. Collections.unmodifiableList is only a read-only view: changes to the original still show through.
List<String> fixed = List.of("a", "b");
// fixed.add("c"); // UnsupportedOperationException
List<String> copy = List.copyOf(mutable); // independent snapshotThe Collections utility class
java.util.Collections has handy static methods: sort, reverse, shuffle, max, min, frequency, nCopies, emptyList, swap and synchronizedList.
Collections.sort(names);
Collections.reverse(names);
int twos = Collections.frequency(List.of(2, 3, 2), 2); // 2
List<String> blanks = Collections.nCopies(3, "");Sequenced collections (Java 21)
Lists, deques, LinkedHashSet, TreeSet, LinkedHashMap and TreeMap now share getFirst(), getLast(), addFirst(), removeLast() and reversed(), instead of each having its own way.
List<String> steps = new ArrayList<>(List.of("plan", "build", "ship"));
steps.getFirst(); // "plan"
steps.getLast(); // "ship"
steps.reversed(); // [ship, build, plan] (a view)Example
List<String> topics = new ArrayList<>(List.of("Spring", "JPA", "Java"));
topics.add("Kafka");
Collections.sort(topics); // [JPA, Java, Kafka, Spring]
Set<String> tags = new TreeSet<>(Set.of("jvm", "api", "orm")); // sorted
Map<String, Integer> views = new HashMap<>();
views.merge("streams", 1, Integer::sum); // count occurrences
int records = views.getOrDefault("records", 0);
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1);
stack.push(2);
stack.pop(); // 2
System.out.println(topics.getFirst()); // Java 21+: JPA
System.out.println(topics.reversed()); // Java 21+: [Spring, Kafka, Java, JPA]Common mistake
Removing items inside a for-each loop throws ConcurrentModificationException. Use list.removeIf(...) or an Iterator's remove().
Under the hood
Big-O cheat sheet: ArrayList get is O(1), add at the end is amortized O(1), insert in the middle is O(n). HashMap get and put are O(1) on average; TreeMap is O(log n). LinkedList has O(n) access and poor cache locality, so ArrayDeque beats it even as a queue. List.of collections reject nulls and throw UnsupportedOperationException if you try to modify them.
Check yourself
Which structure keeps its keys sorted?
How this connects
Know these first
Part of Java from zero, Job-ready backend developer, Crack the Java interview.
Was this lesson helpful?
Finished reading? Mark it complete to track your progress.