Back to Blog
Java

Creating Immutable Lists with Java List.copyOf()

java list copyof: Learn how Java's List.copyOf() creates immutable lists from existing collections, handles nulls, and compares with other list-creation approaches.

JavaImmutable CollectionsList.copyOfJava CollectionsUnmodifiable List
A Java code snippet showing List.copyOf creating an immutable list from a mutable source collection.

Java's List.copyOf() static factory, added in Java 10, creates an unmodifiable list from an existing collection in a single call. It is the direct way to take a mutable collection and produce a list that cannot be structurally modified afterward. For a working developer, java list copyof is the standard answer when you need a defensive snapshot of data that no caller can alter.

Basic Usage of List.copyOf()

The method accepts any Collection and returns a List:

List<String> source = new ArrayList<>(); source.add("alpha"); source.add("beta"); List<String> snapshot = List.copyOf(source);

The returned list contains the same elements in the iteration order of the source collection. Because List.copyOf() copies the element references into a new backing structure, later changes to source do not affect snapshot. This is a true copy, not a view.

Null Elements Are Rejected

List.copyOf() throws NullPointerException if the source collection contains any null element. This is a deliberate difference from Collections.unmodifiableList(), which allows nulls and simply wraps the original list.

List<String> withNull = new ArrayList<>(); withNull.add("alpha"); withNull.add(null); List<String> copy = List.copyOf(withNull); // NullPointerException

This behavior makes List.copyOf() useful when you want to guarantee that a list contains no nulls before it crosses an API boundary. If nulls are a legitimate part of your data model, this method is not the right tool.

The Result Is Structurally Immutable

The returned list cannot be modified. Any attempt to add, remove, or replace an element throws UnsupportedOperationException:

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

This is structural immutability only. The elements themselves are not cloned. If the list holds mutable objects, those objects can still be changed through their own references. List.copyOf() protects the list structure, not the objects inside it.

Comparison with Other List Creation Approaches

The choice of list-creation method depends on whether you need a copy, a view, or a modifiable result.

ApproachNulls allowedCopies elementsResult modifiable
new ArrayList<>(source)YesYesYes
Collections.unmodifiableList(source)YesNo (wraps)No
List.copyOf(source)NoYesNo
List.of(...)NoN/A (varargs)No

Collections.unmodifiableList() wraps the original list, so changes to the source are visible through the unmodifiable view. List.copyOf() copies the data, so the returned list is independent of the source. That independence matters when the source is mutable and you need a stable snapshot.

Performance and Memory Considerations

List.copyOf() copies every element reference into a new underlying array. For a large collection this means one full traversal and one allocation. The copy cost is O(n) in both time and memory. If you are copying a list of millions of entries in a hot path, that cost is real and should be measured rather than assumed negligible.

If the source is already an unmodifiable List created by List.of() or List.copyOf(), the implementation may return the same instance without copying. That is an implementation detail, not a guarantee, so code should not rely on reference equality.

Because the result is immutable, it is safe to share across threads without synchronization. This is often the main reason to use List.copyOf() in concurrent code: a shared immutable list eliminates a whole class of visibility and race-condition problems.

Common Mistakes and Edge Cases

Passing a collection that changes during iteration is a problem. If another thread modifies the source while List.copyOf() is reading it, the behavior depends on the source collection's own thread safety. Copying does not protect against concurrent modification of the source; it only protects the result from later changes.

Another edge case is iteration order. The returned list follows the source's iterator order. For a HashSet, that order is not guaranteed to be stable across runs, so List.copyOf() on a HashSet produces a list whose order is also unspecified. If ordering matters, pass a List or another ordered collection as the source.

Choosing Between List.copyOf() and Alternatives

Use List.copyOf() when you need a snapshot of a collection that no caller can mutate and that must reject nulls. Use Collections.unmodifiableList() when you need a view over an existing list without copying, or when null elements must be allowed. Use new ArrayList<>(source) when the result must remain modifiable.

For small fixed sets of elements, List.of() is simpler than List.copyOf() because it accepts varargs directly. List.copyOf() is the right choice when the input already exists as a collection and you need a defensive, immutable copy. The null-rejection rule is the deciding factor in most real code: if your data can contain nulls, List.copyOf() will fail fast, which is often exactly what you want at an API boundary.

java list copyof: Practical Usage and Code Examples | RYUSLOG DEV