PrepZone Logo
PrepZone

The Collections Framework

How List, Set, Queue and Map relate, and how to pick one from the shape of your problem.

Read these first

Why this matters

  • The framework has dozens of classes, and picking by habit rather than by shape is how an application ends up doing linear scans where a hash lookup would do.
  • Interviews reliably ask you to justify a choice, and "I always use ArrayList" is not a justification.
  • Knowing the interface you should declare — as opposed to the class you instantiate — is what keeps code swappable.

The shape of the framework

Iterable → Collection
Collectionadd, remove, size
ListOrdered, duplicates
SetNo duplicates
QueueEnds-first access
Map — separate branch
Mapput, get, keySet
HashMapNo order
LinkedHashMapInsertion order
TreeMapSorted keys
Map sits outside the Collection branch because it stores pairs rather than single elements. That is why it has no iterator() of its own.

Two roots, not one. Collection covers things holding single elements; Map holds pairs and is deliberately separate because its operations have a different shape.

The four interfaces that matter

  • List — ordered by position, duplicates allowed, indexed access. Use when sequence matters.
  • Set — no duplicates, usually no positional access. Use when membership is the question.
  • Queue / Deque — ordered for processing, with access at the ends. Use for pipelines, buffers and stacks.
  • Map — unique keys mapped to values. Use when you look things up by something other than position.

Choosing, in four questions

AspectWhat you needReach for
An ordered list, mostly read by indexArrayListO(1) indexed access, cache-friendly
Heavy insertion and removal at the endsArrayDequeFaster than LinkedList in practice
Uniqueness, order irrelevantHashSetO(1) membership test
Uniqueness, insertion order keptLinkedHashSetPredictable iteration
Uniqueness, sortedTreeSetO(log n), plus range queries
Key-value lookupHashMapO(1) average get and put
Key-value, sorted keysTreeMapO(log n), navigable
Key-value across threadsConcurrentHashMapConcurrent reads, segmented writes
Highest-priority item firstPriorityQueueA binary heap, O(log n) insert
  • An ordered list, mostly read by index

    What you needArrayList
    Reach forO(1) indexed access, cache-friendly
  • Heavy insertion and removal at the ends

    What you needArrayDeque
    Reach forFaster than LinkedList in practice
  • Uniqueness, order irrelevant

    What you needHashSet
    Reach forO(1) membership test
  • Uniqueness, insertion order kept

    What you needLinkedHashSet
    Reach forPredictable iteration
  • Uniqueness, sorted

    What you needTreeSet
    Reach forO(log n), plus range queries
  • Key-value lookup

    What you needHashMap
    Reach forO(1) average get and put
  • Key-value, sorted keys

    What you needTreeMap
    Reach forO(log n), navigable
  • Key-value across threads

    What you needConcurrentHashMap
    Reach forConcurrent reads, segmented writes
  • Highest-priority item first

    What you needPriorityQueue
    Reach forA binary heap, O(log n) insert

These nine cover the overwhelming majority of real code.

Cost at a glance

The operation that dominates your code is the one to optimise for.

AspectOperationCost by implementation
get by indexArrayList O(1)LinkedList O(n)
add at endArrayList amortised O(1)LinkedList O(1)
add or remove at frontArrayList O(n)ArrayDeque O(1)
containsArrayList O(n)HashSet O(1), TreeSet O(log n)
get by keyHashMap O(1) averageTreeMap O(log n)
iterate in sorted orderTreeMap O(n), freeHashMap requires sorting first
  • get by index

    OperationArrayList O(1)
    Cost by implementationLinkedList O(n)
  • add at end

    OperationArrayList amortised O(1)
    Cost by implementationLinkedList O(1)
  • add or remove at front

    OperationArrayList O(n)
    Cost by implementationArrayDeque O(1)
  • contains

    OperationArrayList O(n)
    Cost by implementationHashSet O(1), TreeSet O(log n)
  • get by key

    OperationHashMap O(1) average
    Cost by implementationTreeMap O(log n)
  • iterate in sorted order

    OperationTreeMap O(n), free
    Cost by implementationHashMap requires sorting first

ArrayList's amortised O(1) append hides an occasional O(n) array copy when it grows.

Declare the interface, instantiate the class

Java
// Good: callers depend on the capability, and you can change your mind
List<String> names = new ArrayList<>();
Map<String, Integer> counts = new HashMap<>();
Set<String> seen = new LinkedHashSet<>();

// Avoid: the concrete type leaks into every signature that touches it
ArrayList<String> rigid = new ArrayList<>();

Declaring List means switching to a LinkedList, or to an immutable List.of(...), is a one-line change. Declaring ArrayList spreads that decision across every method signature it reaches.

Immutable and unmodifiable collections

Three things that are easy to confuse.

Java
// Immutable factories — Java 9+. Reject nulls, no duplicates for Set and Map keys.
List<String> fixed = List.of("a", "b", "c");
Set<Integer> ids = Set.of(1, 2, 3);
Map<String, Integer> ages = Map.of("Ana", 30, "Bo", 25);

// A snapshot copy: independent of the source from this moment on
List<String> snapshot = List.copyOf(mutableSource);

// A view: read-only through this reference, but changes to the source show through
List<String> view = Collections.unmodifiableList(mutableSource);

The distinctions

  • List.of(...) is genuinely immutable, rejects null elements, and is memory-efficient for small sizes.
  • List.copyOf(source) takes a snapshot. Later changes to the source are not visible.
  • Collections.unmodifiableList(source) wraps. The holder cannot modify it, but changes made through the original reference are visible.
  • All three throw UnsupportedOperationException on add, remove or set.

Null tolerance differs

This catches people out because the behaviour is inconsistent across implementations.

AspectImplementationNull handling
ArrayList, LinkedListNulls allowed as elementsAny number of them
HashSet, LinkedHashSetOne null element allowedIt is still a set
TreeSet, TreeMapNo nullsComparison would require calling compareTo on null
HashMapOne null key, many null valuesAllowed
Hashtable, ConcurrentHashMapNo null keys or valuesAmbiguity with absent keys
List.of, Set.of, Map.ofNo nulls anywhereThrows NullPointerException
  • ArrayList, LinkedList

    ImplementationNulls allowed as elements
    Null handlingAny number of them
  • HashSet, LinkedHashSet

    ImplementationOne null element allowed
    Null handlingIt is still a set
  • TreeSet, TreeMap

    ImplementationNo nulls
    Null handlingComparison would require calling compareTo on null
  • HashMap

    ImplementationOne null key, many null values
    Null handlingAllowed
  • Hashtable, ConcurrentHashMap

    ImplementationNo null keys or values
    Null handlingAmbiguity with absent keys
  • List.of, Set.of, Map.of

    ImplementationNo nulls anywhere
    Null handlingThrows NullPointerException

ConcurrentHashMap forbids nulls so that a null return unambiguously means 'absent'.

Methods on the interfaces themselves

Java 8 added default methods that remove most explicit loops.

Java
List<String> names = new ArrayList<>(List.of("ana", "bo", "carl"));

names.removeIf(name -> name.length() < 3);          // safe removal, no iterator needed
names.replaceAll(String::toUpperCase);               // in-place transform
names.forEach(System.out::println);

Map<String, Integer> counts = new HashMap<>();
counts.merge("ana", 1, Integer::sum);                // insert 1, or add to the existing value
counts.computeIfAbsent("bo", key -> expensive(key)); // compute only when missing
counts.getOrDefault("carl", 0);                      // no null check needed
counts.putIfAbsent("dee", 5);
counts.forEach((key, value) -> System.out.println(key + "=" + value));

merge and computeIfAbsent deserve special note: they replace the check-then-act pattern that is both verbose and wrong under concurrency.

Legacy classes to avoid

  • Vector — a synchronised ArrayList. Every method locks, even in single-threaded use. Use ArrayList, or CopyOnWriteArrayList when you genuinely need thread safety.
  • Hashtable — a synchronised HashMap predating the framework. Use HashMap or ConcurrentHashMap.
  • Stack — extends Vector, so it inherits the locking and exposes indexed access a stack should not have. Use ArrayDeque.
  • Enumeration — the pre-Iterator traversal interface, with no removal support.

These remain in the JDK only for backward compatibility. Seeing one in modern code is a signal that the code is old or was copied from something old.

Common misreadings

  • "Map extends Collection." It does not. The two hierarchies are separate by design.
  • "Collections and Collection are the same." Collection is the interface; Collections is a utility class of static helpers.
  • "List.of(...) returns an ArrayList." It returns a compact immutable implementation that throws on modification.
  • "LinkedList is faster for insertions." Only at a position you already hold. Reaching the middle is O(n), and ArrayDeque beats it at both ends.
  • "Every collection accepts null." TreeSet, TreeMap, ConcurrentHashMap and all the of(...) factories do not.
  • "Vector is the thread-safe list to use." Its per-method locking neither performs well nor makes compound operations safe.

Quick recall

Everything you need if you only revisit this box.

  • Pick by shape: duplicates? order? keyed or positional? concurrent?
  • Collection and Map are separate roots. List, Set and Queue extend Collection.
  • Defaults worth memorising: ArrayList, HashMap, HashSet, ArrayDeque — with LinkedHashX for order and TreeX for sorting.
  • Declare the interface, instantiate the class, so the choice stays changeable.
  • List.of is immutable, List.copyOf is a snapshot, Collections.unmodifiableList is a live view. Arrays.asList is a fixed-size array view.
  • Null tolerance varies: TreeSet/TreeMap, ConcurrentHashMap and the of(...) factories reject nulls.
  • Use removeIf, merge, computeIfAbsent and getOrDefault instead of check-then-act loops.
  • Avoid Vector, Hashtable, Stack and Enumeration in new code.

Test yourself

Answer these before moving on — recall is what makes it stick.