Core Java

Java 9 Immutable Collections Example

Photo of YatinYatinJuly 5th, 2017Last Updated: July 5th, 2017
0 1,833 6 minutes read

Hello, in this tutorial we will see another JDK 9 feature i.e. creatingimmutable collections in Java. Java 9 brings the long awaited approach for creating small unmodifiable Collection instances using a concise one line code. As perJEP 269, new convenience factory methods will be included in JDK 9.

1. Introduction

Before Java 9, it was possible to create an immutable view of the collections but only with some utility methods e.g.Collections.unmodifiableCollection(Collection<? extends T> c). For example, let us create an immutable view of Collection in Java 8, with a one liner. It looks bad! Isn’t it?

Test.java

Map<String, String> immutableMap = Collections.unmodifiableMap(new HashMap<String, String>() {{       put("key1", "Value1");       put("key2", "Value2");       put("key3", "Value3");}});

That’s too much code for a simple task and it should be possible to be done in a single expression. Java 9 brings now, something useful with the factory methods for creating immutable collections. Here are the examples of the factory methods:

Java Doc

// Empty Immutable CollectionsList emptyImmutableList = List.of();Set emptyImmutableSet = Set.of();Map emptyImmutableMap = Map.of();// Immutable CollectionsList immutableList = List.of("one", "two");Set immutableSet = Set.of("value1", "value2");Map<String, String> immutableMap = Map.of("key1", "Value1", "key2", "Value2", "key3", "Value3");

2. Java 9 Immutable Collections Example

2.1 What are Collection Factory methods?

A collection factory method in Java is a static method that provides a simple way of initializing animmutableCollection<E>.

Beingimmutable,no elements can be added to,removed from, ormodified inside theCollection<E> after it is initialized. With Java 9, collection factory methods are provided for the following interfaces:List<E>,Set<E> andMap<K, V>

2.2 How are they implemented?

A new package-private utility class that resides in the JDK 9java.util.ImmutableCollections, provides multiple abstract classes that each represent a base for an immutableCollection<E>:AbstractImmutableList<E>,AbstractImmutableSet<E> andAbstractImmutableMap<K, V>.

These abstract classes are used to implement four concrete classes (except forAbstractImmutableMap<K, V> which implements three concrete classes) for eachCollection<E>:

  • List<E>
  1. List0<E>: An immutable implementation of an emptyList<E>
  2. List1<E>: An immutable implementation of aList<E> with one element
  3. List2<E>: An immutable implementation of aList<E> with two elements
  4. ListN<E>:An immutable implementation of aList<E> with a variable amount of elements
  • Set<E>
  1. Set0<E>: An immutable implementation of an emptySet<E>
  2. Set1<E>: An immutable implementation of aSet<E> with one element
  3. Set2<E>: An immutable implementation of aSet<E> with two elements
  4. SetN<E>: An immutable implementation of aSet<E> with a variable amount of elements
  • Map<K, V>
  1. Map0<K, V>: An immutable implementation of an emptyMap<K, V>
  2. Map1<K, V>: An immutable implementation of aMap<K, V> with one key-value entry
  3. MapN<K, V>: An immutable implementation of aMap<K, V> with a variable amount of key-value entries

2.3 What do they improve?

Up until Java 9, there has been no simple universal method to initialize aCollection<E> with initial elements/key-value entries. Previously, developers were required to initialize them as follows (assuming the generic types E, K, and V have been replaced withInteger):

  • List<Integer>
    • The following method is arguably the simplest to initialize aList<Integer> with initial elements, however the result is simply a view of aList<Integer>. We are unable to add to or remove from thisList<Integer>, but we are still able to modify existing elements by usingList#set. For E.g.:List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    • If we wanted ourList<Integer> to be entirely mutable, then we would have to pass it to the constructor of anArrayList<Integer>, for example:List<Integer> mutableList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
  • Set<Integer>
    • ASet<Integer> required more code to initialize with initial elements than aList<Integer> does, which can be seen as:Set<Integer> mutableSet = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
  • Map<Integer, Integer>
    • AMap<Integer, Integer> is arguably the most complicated to initialize with initial key-value entries; however, there are multiple ways to go about it:
      • One method was to first initialize an emptyMap<Integer, Integer> and simply callMap#put to add key-value entries
      • Another method was to use an anonymous class with two curly braces, which would still requireMap#put to be called

2.4 What is the proper syntax to use?

For simplicity, we’re going to look at how to create List, Set, Map with Java 9 Factory Method for Collections.

2.4.1. List

To create a List, we use below static methods:

Java Doc

// for empty liststatic <E> List<E> of()// for list containing one elementstatic <E> List<E> of(E e1)// for list containing two elementstatic <E> List<E> of(E e1, E e2)// for list containing an arbitrary number of elementsstatic <E> List<E> of(E... elements)

For example:

Test.java

List<String> immutableList = List.of();immutableList = List.of("one", "two", "three", null);

If we try to create list withnull element, ajava.lang.NullPointerException will be thrown:

Console Output

Exception in thread "main" java.lang.NullPointerExceptionat java.base/java.util.Objects.requireNonNull(Objects.java:221)at java.base/java.util.ImmutableCollections$ListN.(ImmutableCollections.java:233)at java.base/java.util.List.of(List.java:859)

Because the list created with static factory method is immutable, so if we try to add an element to list, it also throws anjava.lang.UnsupportedOperationException

Test.java

List<String> immutableList = List.of("one", "two", "three");immutableList.add("four");

Console Output

Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:70)at java.base/java.util.ImmutableCollections$AbstractImmutableList.add(ImmutableCollections.java:76)

Solution for problems above:

Test.java

List<String> mutableList = new ArrayList<String>(List.of("one", "two", "three"));mutableList.add("four");mutableList.add(null);

Console Output

// Result: [one, two, three, four, null]

2.4.2. Set

To create a Set, we use below static methods:

Java Doc

// for empty Setstatic <E> Set<E> of()// for Set containing one elementstatic <E> Set<E> of(E e1)// for Set containing two elementstatic <E> Set<E> of(E e1, E e2)// for Set containing an arbitrary number of elementsstatic <E> Set<E> of(E... elements)

For example:

Test.java

Set<String> immutableSet = Set.of();immutableSet = Set.of("one", "two", "three", null);

If we try to create set withnull element, ajava.lang.NullPointerException will be thrown:

Console Output

Exception in thread "main" java.lang.NullPointerExceptionat java.base/java.util.ImmutableCollections$SetN.probe(ImmutableCollections.java:520)at java.base/java.util.ImmutableCollections$SetN.(ImmutableCollections.java:460)at java.base/java.util.Set.of(Set.java:520)

Because the set created with static factory method is immutable, so if we try to add an element to set, it also throws anjava.lang.UnsupportedOperationException

Test.java

Set<String> immutableSet = Set.of("one", "two", "three");immutableSet.add("four");

Console Output

Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:70)at java.base/java.util.ImmutableCollections$AbstractImmutableSet.add(ImmutableCollections.java:280)

Solution for problems above:

Test.java

Set<String> mutableSet = new HashSet<String>(Set.of("one", "two", "three"));mutableSet.add("four");mutableSet.add(null);

Console Output

// Result: [null, four, one, two, three]

2.4.3. Map

  • Map.of()

To create a Map, we use below static methods:

Java Doc

// for empty Mapstatic <K, V> Map<K, V> of()// for Map containing a single mappingstatic <K, V> Map<K, V> of(K k1, V v1)// for Map containing two mappingsstatic <K, V> Map<K, V> of(K k1, V v1, K k2, V v2)// for Map containing up to ten mappingsstatic <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10)

For example:

Test.java

Map<Integer, String> immutableMap = Map.of(1, "one", 2, "two", 3, "three", 4, null );

If we try to create map withnull element, ajava.lang.NullPointerException will be thrown:

Console Output

Exception in thread "main" java.lang.NullPointerExceptionat java.base/java.util.Objects.requireNonNull(Objects.java:221)at java.base/java.util.ImmutableCollections$MapN.(ImmutableCollections.java:677)at java.base/java.util.Map.of(Map.java:1372)

Because the map created with static factory method is immutable, so if we try to put (key, value) pair to map, it also throws anjava.lang.UnsupportedOperationException

Test.java

Map<Integer, String> immutableMap = Map.of(1, "one", 2, "two", 3, "three");immutableMap.put(4, "four");

Console Output

Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:70)at java.base/java.util.ImmutableCollections$AbstractImmutableMap.put(ImmutableCollections.java:557)

Solution for problems above:

Test.java

Map<Integer, String> mutableMap = new HashMap<Integer, String>(Map.of(1, "one", 2, "two", 3, "three"));mutableMap.put(4, "four");mutableMap.put(5, null);

Console Output

// Result:{1=one, 2=two, 3=three, 4=four, 5=null}
  • Map.ofEntries()

If we want to create a Map with more than ten mappings, there is another way: UsingMap.ofEntries() method.

Java Doc

static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries)

To use that method, we use a method for boxing keys and values, suitable for static import:

Java Doc

static <K, V> Entry<K, V> entry(K k, V v)

So, this is way to use them:

Test.java

Map<Integer, String> newImmutableMap = Map.ofEntries(Map.entry(1, "one"), Map.entry(2, "two"), Map.entry(3, "three"));

2.5 Can I use collection factory methods to create mutable objects?

TheCollection<E> created by collection factory methods are inherently immutable, however we are able to pass them to a constructor of an implementation of theCollection<E> to produce a mutable version i.e.

  • List<Integer>

List<Integer> mutableList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

  • Set<Integer>

Set<Integer> mutableSet = new HashSet<>(Set.of(1, 2, 3, 4, 5));

  • Map<Integer, Integer>

Map<Integer, Integer> mutableMap = new HashMap<>(Map.of(1, 2, 3, 4));

3. Pitfalls of Java’s Immutable Collections

The danger of Java’s implementation is that because there is no interface specifically for immutable collections, those immutableSet andMap collections still have the mutable methodsadd/put and remove which will throw anUnsupportedOperationException if called.

Blankly looking atof, it isn’t obvious that the returned collection is immutable. AHashSet would be a reasonable guess since it is by far the most widely used Java set. Java’s ComparableEnumSet.of(...) returns a mutable set. More than a few runtime exceptions are going to be thrown due toof ambiguous return type.

4. Conclusion

The main goal of this article is to discuss the new collection factory methods in Java 9. Of all the new features added to Java 9, the factory methodof is one of the more useful in day-to-day programming but it needs to be used with caution.

5. Download the Eclipse Project

This was an example of Java 9 Immutable Collections. Run the code and the result will be printed in the console window.

Download
You can download the full source code of this example here:Java9 Immutable Collections
Do you want to know how to develop your skillset to become aJava Rockstar?
Subscribe to our newsletter to start Rockingright now!
To get you started we give you our best selling eBooks forFREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to theTerms andPrivacy Policy

Thank you!

We will contact you soon.

Photo of YatinYatinJuly 5th, 2017Last Updated: July 5th, 2017
0 1,833 6 minutes read
Photo of Yatin

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).

Related Articles

Bipartite Graph

Java not equal Example

January 17th, 2020
Bipartite Graph

Java API Tutorial

October 26th, 2020
Bipartite Graph

Java Struct Example

January 8th, 2020
Bipartite Graph

Java Node Example

November 20th, 2019
Bipartite Graph

Java Swing MVC Example

January 26th, 2016
Bipartite Graph

How to call a method in Java

December 26th, 2019
Subscribe
Notify of
guest
I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.

I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.