liveBook · Manning

Collections.unmodifiableXYZ

As explained in 100 Java Mistakes and How to Avoid Them

Collections.unmodifiableXYZ denotes the Java Collections factory methods that expose read-only views of collections.

`Collections.unmodifiableXYZ()` methods expose collection views that callers must treat as read-only. The central mistake is assuming that a collection returned by a method can be modified: calling `add`, `remove`, or similar operations may fail when the result is unmodifiable. Make a separate copy when mutation is required, and document the exceptional APIs that deliberately return mutable collections. section 8.5

Return one mutability contract

Avoid APIs that return a mutable list on one path and an unmodifiable list on another. A caller of `sanitize(input)` may reasonably try `strings.add("Custom...")`; that breaks if an empty-input path returns `Collections.emptyList()` while another path returns an `ArrayList`. Prefer a consistently unmodifiable result, such as `List.copyOf(data)`, or explicitly promise mutability. section 8.5

Use unmodifiable collection APIs defensively:

  • Assume collections returned from other methods are unmodifiable unless their API explicitly promises mutation. section 8.5
  • Copy a returned collection before changing it rather than attempting to modify the returned value. section 8.5
  • Return unmodifiable results from public methods with `Collections.unmodifiableXYZ()`, collection factories such as `List.of()` and `List.copyOf()`, or unmodifiable stream collectors. section 8.5
  • Avoid mutating collection parameters in public APIs unless mutability is an explicit input requirement. section 8.5

Choose the specific wrapper when collection semantics matter. `unmodifiableCollection(list)` does not itself implement `List`, so its `equals()` cannot compare equal to a list under the `List.equals()` contract; symmetry requires the reverse comparison to be false too. `unmodifiableList()` and `unmodifiableSet()` preserve content-based equality appropriate to their respective interfaces. section 7.2

public static <T> List<T> unmodifiableList(List<? extends T> list)

`unmodifiableList` accepts `List<? extends T>` and returns `List<T>`. Besides preventing writes through the result, this can adapt a list of a more specific element type to a read-only list of a more abstract type, avoiding an unchecked cast at a call site. section 5.3.2

For stream results, `Collectors.toList()` yields a de-facto modifiable collection but does not guarantee that property. Use `Collectors.toUnmodifiableList()` when an unmodifiable result is intended; since Java 16, `Stream.toList()` also produces an unmodifiable list. section 8.5

How 100 Java Mistakes and How to Avoid Them covers it

How deep it goes
Explains mutation failures, API-design rules, stream alternatives, equality consequences, and generic type adaptation without wrapper internals.
Examples
Uses a conditionally mutable `sanitize` result, data aggregation followed by `List.copyOf()`, and stream collection to an unmodifiable list.
What it assumes
Assumes familiarity with Java collections and generics while explaining wildcard-based adaptation through `unmodifiableList()`.
What you can do after
Apply it via Java collection factories, wrappers, and stream collectors to expose consistently unmodifiable results and avoid unsupported mutations.
How it connects
Connects collection mutability with `equals()`, generics wildcards, Stream API collectors, `List.copyOf()`, and static analysis annotations.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Java Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.