Return collection as read-only
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Returning a collection as read-only is a common way to protect internal state from accidental modification. The goal is to let callers inspect the data without giving them the ability to add, remove, or replace elements through the returned reference.
In Java, the usual tool is an unmodifiable view or an immutable copy. The right choice depends on whether you want callers to see future changes to the underlying collection or a stable snapshot taken at return time.
Return an Unmodifiable View
The classic pattern is Collections.unmodifiableList or the corresponding wrapper for other collection types:
This prevents callers from modifying the returned list directly:
Understand That Unmodifiable Is Not Immutable
An unmodifiable view does not make the underlying collection immutable. If the owning class changes the internal list later, callers will see those changes through the read-only wrapper.
That can be useful or surprising depending on the API design.
If you want a stable snapshot instead, return a copy:
This creates a separate read-only result representing the state at that moment.
Choose Between View and Snapshot
Use an unmodifiable view when:
- the collection is part of current live state
- you want callers to observe later changes
- you want to avoid copying overhead
Use an immutable copy when:
- callers should receive a stable snapshot
- you do not want later internal mutations to be visible
- API predictability matters more than avoiding the copy
This is a design decision, not just a syntax choice.
Defensive Encapsulation Still Matters
Returning the raw internal collection directly is the dangerous version:
That lets outside code mutate internal state freely, which breaks encapsulation and can make debugging much harder.
The whole point of read-only returns is to avoid that leak of ownership.
Read-Only APIs Communicate Ownership
Returning a read-only collection is not only a safety mechanism. It also communicates that the caller may inspect the data but does not own the right to mutate the provider's internal state. That makes API intent clearer.
That kind of ownership signaling is one reason read-only returns improve maintainability as well as safety.
It also makes later refactoring safer because the mutation boundary is much more obvious to callers.
That design clarity is a practical benefit, not just a theoretical one.
That is exactly the kind of boundary that reduces accidental coupling between classes.
Common Pitfalls
- Returning the raw internal collection and assuming callers will behave.
- Confusing an unmodifiable view with a truly immutable snapshot.
- Forgetting that mutable elements inside a read-only collection can still be changed individually.
- Using snapshots everywhere without considering the cost for large collections.
- Exposing internal state in APIs and then being surprised by accidental external mutations.
Summary
- Return read-only collections to protect internal state.
- '
Collections.unmodifiableListprovides a read-only view of an existing collection.' - '
List.copyOfreturns an immutable snapshot copy.' - Choose between view and snapshot based on whether callers should see later changes.
- Avoid returning raw internal collections directly.
Related reading
- Rights to read /dev/tty0 from pod
- RolesAllowed vs. PreAuthorize vs. Secured
- Root password inside a Docker container
- running a container with runAsNonRoot and add capabilities
- Return HTTP code 200 from Spring REST API
- Return ResponseEntity vs returning POJO
- S3 - Access-Control-Allow-Origin Header
- S3 Bucket Policy to make a specific sub folder public and everything else private?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.