In this article we will see how to create immutable collections with List, Set, Map, what are the flaws in existing approaches and how Java 9 solved them.
1. Overview :
To create immutable collections java.util.Collections
class provides following methods in Java.
unmodifiableList()
– Creates not modifiable (immutable) List.unmodifiableSet()
– Creates not modifiable (immutable) Set.unmodifiableMap()
– Creates immutable Map.
Example 1 – Creating immutable List, Set and Map
Following example shows you how to create immutable list, set and map.
public class ImmutableCollectionBeforeJDKDemo { public static void main(String[] args) { //Creating Unmodifiable List List<String> list = new ArrayList<>(); list.add("Peter"); list.add("Gerhard"); list = Collections.unmodifiableList(list); System.out.println(list); //list.add("Philip"); /*if above line uncomment will get java.lang.UnsupportedOperationException*/ //Creating Unmodifiable Set Set<String> set = new HashSet<>(); set.add("Peter"); set.add("Gerhard"); set = Collections.unmodifiableSet(set); //set.add("Philip"); /*if above line uncomment will get java.lang.UnsupportedOperationException*/ //Creating immutable Map Map<Integer, String> map = new HashMap<>(); map.put(1, "Peter"); map.put(2, "Gerhard"); map = Collections.unmodifiableMap(map); System.out.println(map); map.put(3, "Philip"); /*if above line uncomment will get java.lang.UnsupportedOperationException*/ } }
Flaw in Example 1 :
In example 1 you see how to create immutable collections, you can still modify the collection, to see how let’s take set from example. See the following example, you cannot modify the set1. However, you can still use the set1 variable to modify the set2 and modifications will be reflected when you read the set1 and set2.
public class ImutableModifiableSetDemo { public static void main(String[] args) { // Creating Unmodifiable Set Set<String> set1 = new HashSet<>(); set1.add("Peter"); set1.add("Gerhard"); Set<String> set2 = Collections.unmodifiableSet(set1); System.out.println("set1 => " + set1); System.out.println("set2 => " + set2); set1.add("Philip");// set2 also will effect System.out.println("set1 => " + set1); System.out.println("set2 => " + set2); } }
Output :
set1 => [Gerhard, Peter] set2 => [Gerhard, Peter] set1 => [Gerhard, Peter, Philip] set2 => [Gerhard, Peter, Philip]
Example 2 – Converting array to immutable collection :
Following example shows you how to create immutable list and immutable set in Java using arrays and by converting them to Collection.
public class ArraysAsListDemo { public static void main(String[] args) { // Using Arrays.asList() creating List List<String> list = Collections.unmodifiableList( Arrays.asList("Peter", "Gerhard")); System.out.println("list => "+list); // Using Arrays.asList() creating Set Set<String> set = Collections.unmodifiableSet( new HashSet<>(Arrays.asList("Peter", "Gerhard"))); System.out.println("set=> "+set); } }
Output :
list => [Peter, Gerhard] set => [Gerhard, Peter]
Example 3 – Converting array to immutable collection using double brace :
Following example shows you how to create immutable list and immutable set in Java using double braces.
public class AnonymousClassDoubleBraceDemo { public static void main(String[] args) { // Using double-brace technique List<String> list = Collections.unmodifiableList( new ArrayList<>(){{add("Peter"); add("Gerhard");}}); System.out.println("list=> "+list); Set<String> set = Collections.unmodifiableSet( new HashSet<>(){{add("Peter"); add("Gerhard");}}); System.out.println("set=> "+set); } }
Output :
list => [Peter, Gerhard] set => [Gerhard, Peter]
Example 4 – Creating immutable collections using Java streams :
Following example shows you how to create immutable list and immutable set in Java using Java 8 Streams.
public class Java8StreamsImmutableCollectionDemo { public static void main(String[] args) { // Using a Java 8 stream Creating immutable List List<String> list = Stream.of("Peter", "Gerhard").collect( Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); System.out.println("list=> "+list); // Using a Java 8 stream Creating immutable Set Set<String> set = Stream.of("Peter", "Gerhard").collect( Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet)); System.out.println("set=> "+set); } }
Output :
list => [Peter, Gerhard] set => [Gerhard, Peter]
Flaws in Example 2, Example 3 and Example 4 :
These techniques Example 1, Example 2 and Example 3 are inefficient. For example, just to hold two values in a collection, these techniques create multiple objects backing to hold the values and little verbose also.
Conclusion :
In this article we have seen how to create immutable collections in java in different approaches and what are the flaws behind them with different examples. These flaws are identified and to provide a simple way to create immutable collections, introduced Convenience Factory Methods for Collections in JDK 9.