Stage 3: Core APIs, lesson 2 of 12

Collections framework: choosing the right one

Intermediate3 min read@since 21Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

The core interfaces:

  • List: ordered, allows duplicates. Default choice: ArrayList. LinkedList is rarely better.
  • Set: no duplicates. HashSet (fast, no order), LinkedHashSet (insertion order), TreeSet (sorted).
  • Map: key to value. HashMap, LinkedHashMap, TreeMap, and ConcurrentHashMap when several threads share it.
  • Queue and Deque: ArrayDeque for stacks and queues, PriorityQueue for "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.

Java
Iterator<Order> it = orders.iterator();
while (it.hasNext()) {
    if (it.next().isCancelled()) it.remove();   // safe
}
orders.removeIf(Order::isCancelled);            // simpler

Immutable 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.

Java
List<String> fixed = List.of("a", "b");
// fixed.add("c");              // UnsupportedOperationException
List<String> copy = List.copyOf(mutable);   // independent snapshot

The Collections utility class

java.util.Collections has handy static methods: sort, reverse, shuffle, max, min, frequency, nCopies, emptyList, swap and synchronizedList.

Java
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.

Java
List<String> steps = new ArrayList<>(List.of("plan", "build", "ship"));
steps.getFirst();       // "plan"
steps.getLast();        // "ship"
steps.reversed();       // [ship, build, plan] (a view)

Example

Java
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

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.