Static Factory Methods on Collections - Immutable Sets, Lists and Maps
Java 9 supports creating immutable Sets, Maps and Lists through the provision of static factory methods on the respective Interfaces.
If you have predefined data and want it to be unchanged at runtime then you could add the same to the relevant Immutable collection. Any attempt to modify the collection would throw UnsupportedOperationException
. If the data needs to be added or removed then go with existing mutable collection classes.
Java 9 has provided multiple overloaded static factory methods under List, Set and Map interfaces. These allow creating immutable collections with zero elements to 10 elements, as well as another method that accepts varargs
argument to create an immutable collection with an arbitrary number of elements.
The following declaration is the definition of static methods from List
interface. The same goes for Set
and Map
interfaces.
Static<E> List<E> of()
Static<E> List<E> of(E... elements)
Static<E> List<E> of(E e)
Static<E> List<E> of(E e1, E e2)
…
Static<E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)
In addition, Map
interface also provides another static method to create an immutable list through elements of type Map.Entry
.
static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries)
Example
Java 9
import java.util.List;
. . .
List<String> immutableListJava9 =
List.of(“Java9”,”Modular”,”Programming”);
Java 8
List<String> mutableList =
Arrays.asList(“Java9”,”Modular”,”Programming”);
List<String> immutableListJava8 =
Collections.UnmodifiableList(mutableList);
In the same lines,
Collections.UnmodifiableSet
and Collections.UnmodifiableMap
can be used.
Understanding the changes made in JDK to support Immutable Collections
Java 9 introduced a non-public class called ImmutableCollections
.This class contains multiple static inner classes named List0, List1, List2,
and ListN
for creating instances of Immutable lists for zero elements, one element, two elements, and three or more elements respectively.
Similarly, for creating Immutable sets ImmutableCollections
class contains immutable inner classes named Set0, Set1, Set2
and SetN
for creating instances of an Immutable set with Zero elements, one element, two elements and three or more elements respectively.
As observed, for Immutable map creation there available only three immutable inner classes: Map0, Map1 and MapN,
for no entries, one entry and two or more elements respectively.
Fig : Class diagram representing Hierarchy of Immutable Collections of Java 9 as well already existing ArrayList
.
For instance, consider creating immutable lists with zero Elements
List<String> zeroStrings = List.of();
Internally calls ImmutableCollections.List0.instance();
In the similar fashion, an immutable set with two elements
Set<String> iSetWithTwoElements = Set.of(“java”,”9”);
Internally calls ImmutableCollections.Set2<>(e1, e2);
The varargs
method variant also accepts an array of elements and returns immutable list with array contents as elements. However, when the array itself is required as an entry to the immutable list rather than splitting individual elements then do as follows.
For considering each element of the array as an entry into the immutable collection convert the array using the following approach.
String[] strArray = {“java”,”9”};
List<String> immutableListFromArray = List.of(strArray);
The resulted list size is equivalent to the size of the array from which the list has been created.
For considering array object itself as an entry into the immutable collection.
List<String[]> iListOfArrays = List.<String[]>of(strArray);
The created immutable list from the above snippet has size ONE with the passed array being stored at index 0.
More about Immutable Collections
ImmutableCollections holds int attribute called “SALT” that is used for fluctuating the order of the collection. This happens once per lifetime of the JVM. SALT is initialized once and stays constant until the next run of the JVM. This makes sure the iteration order to be varied between the runs.
The iteration order needs to be randomized for helping in identifying the code that depends on the iteration order. The following illustrates the mentioned phenomena.
Fig: Illustration - 1 of the iteration order
Code from Fig: Illustration -1 creates two sets and shows how the order is maintained. whereas, Illustration - 2 describes the same immutable sets maintaining different order from the Illustration - 1.
Fig: Illustration - 2 of the iteration order
Even though, Immutable Collections let you create unmodifiable List, Set and Map
if the elements within the created collection can be modified if they are mutable.
For instance, the following illustrates creating a mutable List, an ArrayList instance, and adding two String variables “1” and “2”. Following, we created an ImmutableList with earlier created mutable list as a single element. Now, we could be able to remove the elements from the mutable List and consequently the ImmutableList has been changed. Evidently, the underlying mutable list is still modifiable. The same behaviour can be observed with collections created using Collections.UnmodifiableList/Set/Map.
Fig : Describing ImmutableCollections with mutable entries within.
However, adding elements directly to ImmutableList
, but not the underlying mutable one if any, would cause UnSupportedOperationException
.
Memory Footprint
Immutable collections created using conventional approach would occupy more memory compared to static factory methods introduced in Java 9.
Set<String> set = new HashSet<>();
set.add(“a”);
set.add(“b”);
Set = Collections.unmodifiableSet(set);
The above code creates a HashSet
with two entries, an Unmodifiable Wrapper. HashSet contains a HashMap
, an array for table of hash buckets, and Node instance to hold each entry. Evidently, SIX objects are in picture and total memory footprint is around 152 bytes. Whereas the usage of Set.of()
to perform the above operation creates an overhead of just 20 bytes.
Hence it is advised to consider using immutable collections instead of legacy collections when the dataset is predefined or wouldn't allowed to be changed once created.
StackTrace filtering
Improved Process API
Improved Optional