Learnitweb

Create unmodifiable copy using copyOf method

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 of Map both keys and values cannot be added, removed, or updated.
  • null elements are not allowed. If you try to create them with null elements, NullPointerException is thrown. In case of Map, keys and values can not be null.
  • 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 the copyOf method, IllegalArgumentException is thrown. In case of Map, duplicate keys are not allowed at creation time.
  • In case of Set the iteration order is unspecified. In case of List, 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.