1. Introduction
In Java 10, the Set.copyOf
, List.copyOf
, Map.copyOf
static factory methods provide a convenient way to create unmodifiable collections (Set, List and Map).
A collection is considered as unmodifiable if elements cannot be added, modified or removed. An unmodifiable collection hold the same data as long as the reference exists to the collection.
The instances created by these methods have following characteristics:
- They are unmodifiable. This means, elements can not be added or removed. If you try to call a mutator method, this will cause in
UnsupportedOperationException
to be thrown. However, if the individual elements of the collection are mutable, this may result in inconsistent behavior and the collection may appear to be changed. In case ofMap
both keys and values cannot be added, removed, or updated. null
elements are not allowed. If you try to create them withnull
elements,NullPointerException
is thrown. In case ofMap
, keys and values can not benull
.- They are serializable if all elements of the collections are serializable. In case of Map, both keys and values should be serializable.
- In case of
Set
, duplicate elements are not allowed at creation time. If duplicate elements are passed to thecopyOf
method,IllegalArgumentException
is thrown. In case ofMap
, duplicate keys are not allowed at creation time. - In case of
Set
the iteration order is unspecified. In case ofList
, the order of elements is same as the order of the provided arguments. - They are value-based. Factory methods can create new instances or reused existing instances. Callers of these methods should not make any assumption about the identity of the returned instances and operations like (reference equality (==), identity hash code) are not reliable and should be avoided.
A collection that can be modified maintains bookkeeping data to support modifications. This bookkeeping data stored in the collection is an overhead. An unmodifiable collection does not need this extra bookkeeping data. Unmodifiable collection instances generally consume much less memory than modifiable collection instances that contain the same data.
2. Use Cases of Unmodifiable Collection
An unmodifiable collection is recommended for following use cases:
- Collections that are initialized from constants that are known when the program is written.
- Collections that are initialized at the beginning of a program from data that is read from a configuration file or that is computed.
3. The signature of copyOf
Following are the signatures of copyOf
methods of List
, Set
and Map
:
static <E> Set<E> copyOf(Collection<? extends E> coll)
Returns an unmodifiable Set containing the elements of the given Collection.static <E> List<E> copyOf(Collection<? extends E> coll)
Returns an unmodifiable List containing the elements of the given Collection, in its iteration order.static <K,V> Map<K,V> copyOf(Map<? extends K,? extends V> map)
Returns an unmodifiable Map containing the entries of the given Map.
Note: When using Set.copyOf
to form a Set
out of a List
with duplicate elements, no exceptions are raised. Rather, one of the duplicate elements is arbitrarily added to the resultant Set
.
When the collection targeted for duplication is mutable, the copyOf
function generates an immutable collection that mirrors the original. This ensures that any alterations made to the original collection, such as additions or deletions, do not impact the copied version.
However, if the original collection is already immutable, the copyOf
function merely returns a pointer to the original collection. The purpose of copying is to safeguard the returned collection from modifications to the original. Yet, if the original collection is immutable, there’s no necessity to duplicate it.
4. Difference between Collections.unmodifiableList and copyOf
Collections.unmodifiableList
return an unmodifiable view of the collection. The underlying collection is still modifiable, so it is actually not mutable. Let us see an example.
public class UnmodifiableListExample { public static void main(String args[]) { // Create a list List<String> names = new ArrayList<>(); // add elements to the list names.add("one"); names.add("two"); names.add("three"); //create unmodifiableView List<String> unmodifiableView = Collections.unmodifiableList(names); System.out.println("unmodifiableView before modification: " + unmodifiableView); //modifiy the underlying collection names.add("four"); //print the unmodifiable view again System.out.println("unmodifiableView after modification: " + unmodifiableView); } }
Output
unmodifiableView before modification: [one, two, three]
unmodifiableView after modification: [one, two, three, four]
Let us now modify the program to use copyOf
.
public class UnmodifiableListExample { public static void main(String args[]) { // Create a list List<String> names = new ArrayList<>(); // add elements to the list names.add("one"); names.add("two"); names.add("three"); //create unmodifiableView List<String> unmodifiableList = List.copyOf(names); System.out.println("unmodifiableList before modification: " + unmodifiableList); //modifiy the underlying collection names.add("four"); //print the unmodifiableList again System.out.println("unmodifiableList before modification: " + unmodifiableList); } }
Output
unmodifiableList before modification: [one, two, three]
unmodifiableList after modification: [one, two, three]
5. Conclusion
In summary, the copyOf
method in Java allows you to create an immutable (unmodifiable) copy of a collection or array. By using this method, you can ensure that the copied data remains unchanged, promoting better code reliability and preventing accidental modifications.