Close

Java 10 - Collection Changes | APIs for Creating Unmodifiable Collections

[Last Updated: Apr 13, 2018]

In Java 10, following new static methods to create unmodifiable collections have been added.

java.util.List: <E> List<E> copyOf(Collection<? extends E> coll)
java.util.Set: <E> Set<E> copyOf(Collection<? extends E> coll)
java.util.Map: <K,V> Map<K,V> copyOf(Map<? extends K, ? extends V> coll)

Since the return collection/map is unmodifiable, attempting to modify it (adding/removing new elements) throws UnsupportedOperationException.

These methods create shallow copy of the provided collection. That means the collection elements (or key/values in case of map) are not copied/cloned, so if the original elements (if mutable) are changed they will reflect the changes in the new collection and vice-versa.

These methods throw NullPointerException, if the provided collection has null elements.

Examples

List.copyOf(collection)

public class ListCopyOfExample {
  public static void main(String[] args) {
      List<Integer> list = new ArrayList<>();
      list.add(1);
      list.add(2);
      List<Integer> integers = List.copyOf(list);
      System.out.println(integers);
  }
}
[1, 2]

Set.copyOf(collection)

public class SetCopyOfExample {
  public static void main(String[] args) {
      List<Integer> list = new ArrayList<>();
      list.add(1);
      list.add(2);
      Set<Integer> integers = Set.copyOf(list);
      System.out.println(integers);
  }
}
[2, 1]

Map.copyOf(map)

public class MapCopyOfExample {
  public static void main(String[] args) {
      Map<Integer, String> map = new HashMap<>();
      map.put(3,"three");
      map.put(4, "four");
      Map<Integer, String> map2 = Map.copyOf(map);
      System.out.println(map2);
  }
}
{4=four, 3=three}

When to use copyOf() methods?

Let's see the List.CopyOf() code snippet:

 static <E> List<E> copyOf(Collection<? extends E> coll) {
    if (coll instanceof ImmutableCollections.AbstractImmutableList) {
        return (List<E>)coll;
    } else {
        return (List<E>)List.of(coll.toArray());
    }
 }

As seen above, this method is just a helper around List.of() method. Note that the List.of() also return a type AbstractImmutableList, so if the provided collection is created by List.of() methods then it is return as it is.

Also the copyOf() methods allow us to assign the provided collection to a collection having elements of super type (covariant assignment). In above snippet, there are also 'unchecked' castings, but since the returned collection is immutable this cannot lead to heap pollution situation.

Consider following example without CopyOf method:

public class ListExample1 {
  public static void main(String[] args) {
      List<Integer> numbers = new ArrayList<>();
      numbers.add(2);
      List<? extends Number> s = numbers;
      aLibOperation((List<Number>) s);
      Integer number = numbers.get(0);//heap pollution
  }

  public static void aLibOperation(List<Number> numbers){
      numbers.set(0, new BigDecimal(5));
  }
}
java.lang.ClassCastException: java.base/java.math.BigDecimal cannot be cast to java.base/java.lang.Integer
at com.logicbig.example.ListExample1.main(ListExample1.java:13)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.logicbig.invoker.MethodCaller.main(MethodCaller.java:31)

Above ClassCastException will be a surprise for a developer who is not aware of aLibOperation() code.

Now let's use CopyOf() method:

public class ListExample2 {
  public static void main(String[] args) {
      List<Integer> numbers = new ArrayList<>();
      numbers.add(2);
      aLibOperation(List.copyOf(numbers));
      Integer number = numbers.get(0);
  }

  public static void aLibOperation(List<Number> numbers){
      numbers.set(0,1);
  }
}
java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.set(AbstractList.java:136)
at com.logicbig.example.ListExample2.aLibOperation(ListExample2.java:15)
at com.logicbig.example.ListExample2.main(ListExample2.java:10)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.logicbig.invoker.MethodCaller.main(MethodCaller.java:31)

As seen in above output, the UnsupportedOperationException is thrown at the right place (aLibOperation) where a wrong operation is performed, instead of very late unexpected ClassCastException.

Set.CopyOf() and Map.copyOf() have the similar behavior as we saw above.

List.CopyOf() vs Collections.unmodifiableList()

Collections.unmodifiableList() (and other similar methods) returns a unmodifiable view of the source collection, so changes made to the source collection reflects in it. Whereas, in case of List.copyOf() method, if the source collection is subsequently modified, the returned List will not reflect such modifications.

public class WithCollectionsUtilExample {
  public static void main(String[] args) {
      List<Integer> list = new ArrayList<>();
      list.add(1);
      list.add(2);
      List<Number> unmodifiableList = Collections.unmodifiableList(list);
      List<Integer> copyOfList = List.copyOf(list);
      //modifying the source list
      list.add(3);
      System.out.println("unmodifiableList: " + unmodifiableList);
      System.out.println("copyOfList: " + copyOfList);
  }
}
unmodifiableList: [1, 2, 3]
copyOfList: [1, 2]

Example Project

Dependencies and Technologies Used:

  • JDK 9.0.1
Java 10 Collection API Changes Examples Select All Download
  • java-10-collection-changes
    • src
      • com
        • logicbig
          • example
            • ListCopyOfExample.java

    See Also