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
UnsupportedOperationExceptionto 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 ofMapboth keys and values cannot be added, removed, or updated. nullelements are not allowed. If you try to create them withnullelements,NullPointerExceptionis 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 thecopyOfmethod,IllegalArgumentExceptionis thrown. In case ofMap, duplicate keys are not allowed at creation time. - In case of
Setthe 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.
