Course outline

Java / Intermediate / Lesson 7

Collections, equality, and ownership

Choose list, set, and map behavior from the query contract. Reason about ordering, duplicate keys, mutable elements, and safe iteration.

About 5 min readContent reviewed
On this page

Lesson goals

  • Select a collection by required semantics.
  • Explain equality and iteration-order guarantees.
  • Distinguish unmodifiable views from independent copies.

Choose behavior before implementation

List preserves a sequence and permits duplicate elements; Set models membership without duplicate equal elements; Map associates each key with at most one value. These are semantic contracts, not interchangeable containers. For ordered invoice lines use a list, for visited identifiers a set, and for counts by product a map. Define whether null is valid rather than relying on inconsistent implementation policies.

Choose an implementation after identifying access patterns. ArrayList supports efficient indexed access and append in common workloads, while linked structures have different traversal and insertion costs. Hash-based collections do not promise sorted order. Tree-based collections use an ordering relation, and linked hash collections preserve a documented encounter order. Read each implementation's contract before making observable output depend on iteration.

Explain list mutability and snapshots

List.of creates an unmodifiable list and rejects null elements. It is not a resizable ArrayList. The example creates a mutable copy, appends one value, and prints both sizes. The original size remains two while the copy grows to three. Elements are strings, so shallow sharing is harmless here; copying a list of mutable objects would still share those objects.

Collections.unmodifiableList can wrap a mutable list as a read-only view, but later changes through the original list remain visible. List.copyOf provides an unmodifiable snapshot of the collection's elements at copying time, with its documented null policy. Neither mechanism recursively freezes an object graph. State whether an API returns a live view, a snapshot, or owned mutable data.

java A mutable copy has independent list structure
public class Main {

 public static void main(String[] args) throws Exception {
var original=java.util.List.of("a","b");
var copy=new java.util.ArrayList<>(original);
copy.add("c");
System.out.println(original.size());
System.out.println(copy.size());
 }
}
Expected output
2
3

Adding to copy changes its structure only. Both lists still reference the same immutable string elements.

Aggregate values with a stated key policy

Map.merge expresses a common update: insert an initial count or combine it with an existing count. The example counts red, blue, red and uses TreeMap so printed keys are sorted. The output is deterministic because ordering is part of the chosen implementation contract, not because a small HashMap happened to print the desired order.

A duplicate key does not create another map entry. put replaces the value, while merge applies a specified rule. Decide whether replacement, combination, or rejection is appropriate for the domain. Equality and hashCode must agree for hash-based keys, and equality-relevant key state must remain stable while stored. Prefer immutable identifiers over mutable customer objects as keys.

java Count with explicit sorted output
public class Main {

 public static void main(String[] args) throws Exception {
var counts=new java.util.TreeMap<String,Integer>();
for(String color:java.util.List.of("red","blue","red")) {
 counts.merge(color,1,Integer::sum);
}
System.out.println(counts);
 }
}
Expected output
{blue=1, red=2}

The two red values combine under one key, and natural String ordering places blue before red.

Modify a collection through the right boundary

Fail-fast iterators can detect some structural modifications made outside their permitted protocol and throw ConcurrentModificationException. This is not a thread-safety guarantee and can happen in a single thread. Use an iterator's supported removal operation, removeIf, or collect a new result instead of mutating the source during an enhanced loop. Choose a method that expresses the intended filtering operation clearly.

Concurrent collections have different iteration and update semantics. A ConcurrentHashMap can coordinate individual operations, but a sequence such as containsKey followed by put is not automatically one atomic decision. Use appropriate compound APIs or a wider synchronization design. Do not replace every collection with a concurrent implementation while leaving the application's multi-step invariants unexplained.

Test boundaries and observable order

Collection tests should cover empty input, duplicates, absent keys, equality collisions, and the intended null policy. If output order matters, assert the specified order rather than sorting in the test to conceal an unordered implementation. For a membership-only operation, avoid asserting a particular iteration order that the contract does not require.

Ownership affects both correctness and performance. Copying a small list at an API boundary may remove a large class of accidental-mutation bugs. For large data, measure and document whether a view or snapshot is required and who can modify the backing source. A correct data structure choice explains element semantics, mutation permission, ordering, and the cost of the operations the application actually performs.

Common errors and fixes

UnsupportedOperationException

Why it happens
Code tried to mutate an unmodifiable collection.
How to fix it
Use an owned mutable copy or redesign the operation to return a new result.
Prevent it next time
Document collection mutability in APIs.

ConcurrentModificationException

Why it happens
Iteration and structural mutation used incompatible protocols.
How to fix it
Use removeIf, supported iterator removal, or a new collection.
Prevent it next time
Test filtering with multiple matching elements.

A map key cannot be found after editing it

Why it happens
Mutable equality/hash state changed while the key was stored.
How to fix it
Use a stable immutable key or remove and reinsert under a valid ownership policy.
Prevent it next time
Review equals and hashCode together.

Check your understanding

1. Why does copying List<MutableItem> not freeze every item?

Show worked solution

The list structure is copied, but references to elements remain shared. Use immutable element types, defensive element copies, or a clearly documented shared-ownership contract if callers must not modify internal state.

2. A report must print product codes sorted with their counts. Choose a representation.

Show worked solution

A TreeMap can accumulate counts and iterate by its comparator order. Alternatively sort entries explicitly at presentation. Do not rely on HashMap iteration, and define whether codes compare lexically or under a domain-specific ordering.

Key takeaways

  • Collection interfaces encode sequence, membership, or association.
  • Unmodifiable does not imply deep immutability.
  • Ordering and compound atomicity require explicit contracts.

Official references