Immutable Set in Java
This post will discuss various methods to create an immutable set in Java. Immutable Sets have many advantages over their mutable siblings, like they are thread-safe, more memory-efficient, and can be passed to untrusted libraries without any side effects.
Unmodifiable Sets are “read-only” wrappers over other collections. They do not support any modification operations such as add, remove, and clear, but their underlying collection can be changed. Sets that additionally guarantee that no change in the Collection object will ever be visible are referred to as immutable. When we don’t expect to modify a set, it’s a good practice to defensively copy it into an immutable set.
One of the simplest and most widely used ways to create an immutable set in Java is to use the Collections.unmodifiableSet() method, which takes an existing set and returns a view of it that cannot be modified. However, it is unsafe to use as the returned set is only truly immutable if nobody holds a reference to the original collection. This means that we can still modify the original set, which will affect the unmodifiable view as well. The returned set is also inefficient as it still has the overhead of mutable collections, including concurrent modification checks, extra space in hash tables, etc.
There are several other ways to create an immutable set in Java, using both built-in methods and external libraries. These are discussed below in detail:
1. Using Set.of() method
Several collection factory methods have been added in Java 9 that allows for easy initialization of immutable collections with specified elements. These factory methods are only available for List, Set, and Map interfaces. The returned collection is structurally immutable, i.e., elements cannot be added, removed, or replaced from the collection. Any modification operation will throw an UnsupportedOperationException.
We can use the Set.of() static factory method to create an immutable set with one or more elements. For example, if we want to create an immutable set with three elements, we can do like:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import java.util.Set; class Main { public static void main(String[] args) { // create an immutable set using Set.of() Set<String> immutableSet = Set.of("C", "C++", "Java"); try { // any attempt to modify the set will result in UnsupportedOperationException immutableSet.add("C#"); } catch (UnsupportedOperationException ex) { System.out.println("java.lang.UnsupportedOperationException"); } System.out.println(immutableSet); // [C++, C, Java] } } |
This method has the benefit of being straightforward and easy to use. However, it has some limitations: it requires Java 9 or above, it does not accept null values, and it does not preserve the order of the elements. Also, if the contained elements are mutable, it may cause the Collection’s contents to appear to change. For example:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; class Main { public static void main(String[] args) { List<String> list1 = new ArrayList<>(Arrays.asList("C", "C++", "Java")); List<String> list2 = new ArrayList<>(Arrays.asList("Python", "PHP")); Set<List<String>> immutableSet = Set.of(list1, list2); System.out.println(immutableSet); // [[C, C++, Java], [Python, PHP]] list2.add("Go"); System.out.println(immutableSet); // [[C, C++, Java], [Python, PHP, Go]] } } |
2. Using Guava’s ImmutableSet class
Another way to create an immutable set in Java is to use Guava’s ImmutableSet class. This class provides several methods to create an immutable set from an existing set, an array, an iterable, or a builder. Unlike Collections.unmodifiableSet(), which is a view of a separate collection that can still change, an instance of ImmutableSet contains its own private data and will never change.
1. Using ImmutableSet.of() method
The ImmutableSet.of() method returns an immutable set containing the given elements in order. This method has seven overloaded versions, each with a different number of parameters. The last version uses varargs, a special syntax that allows passing an array of elements as a single argument.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import com.google.common.collect.ImmutableSet; class Main { public static void main(String[] args) { // create an immutable set containing the given elements, in order ImmutableSet<String> immutableSet = ImmutableSet.of("C", "C++", "Java"); try { // any attempt to modify the set will result in // an UnsupportedOperationException immutableSet.add("C#"); } catch (UnsupportedOperationException ex) { System.out.println("java.lang.UnsupportedOperationException"); } System.out.println(immutableSet); // [C++, C, Java] } } |
That’s all about immutable Set in Java.
2. Using ImmutableSet.Builder() method
Guava also provides a Builder for creating an immutable set instance. The ImmutableSet.builder() method returns an ImmutableSet.Builder object, which has methods like add, addAll, build, etc. We can use add() to insert a single element, or addAll() to insert a collection of elements to the builder. Finally, we need to call the build() method that return the immutable set with the elements we added. For example:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import com.google.common.collect.ImmutableSet; import java.util.HashSet; import java.util.Arrays; import java.util.Set; class Main { public static void main(String[] args) { Set<String> mutableSet = new HashSet<>(Arrays.asList("C","C++")); // get a builder for creating an immutable set instance ImmutableSet<String> immutableSet = new ImmutableSet.Builder<String>() .addAll(mutableSet) // adds each element of mutable set to the builder .add("Java") // add a single element to the builder .build(); // build() returns a newly created ImmutableSet try { // any attempt to modify the set will result in UnsupportedOperationException immutableSet.add("C#"); } catch (UnsupportedOperationException ex) { System.out.println("java.lang.UnsupportedOperationException"); } // any changes made to the original set will not be reflected in the immutable set mutableSet.add("C#"); System.out.println(immutableSet); // [C++, C, Java] } } |
3. Using ImmutableSet.copyOf() method
The ImmutableSet.copyOf() method returns an immutable set containing the elements of the specified collection, array, or iterator, in the order they appear in it. For example:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import com.google.common.collect.ImmutableSet; import java.util.HashSet; import java.util.Arrays; import java.util.Set; class Main { public static void main(String[] args) { Set<String> mutableSet = new HashSet<>(Arrays.asList("C", "C++", "Java")); // ImmutableSet.copyOf() creates a copy of the mutable set ImmutableSet<String> immutableSet = ImmutableSet.copyOf(mutableSet); try { // any attempt to modify the set will result in UnsupportedOperationException immutableSet.add("C#"); } catch (UnsupportedOperationException ex) { System.out.println("java.lang.UnsupportedOperationException"); } // any changes made to the original set will not be reflected in the immutable set mutableSet.add("Go"); System.out.println(immutableSet); // [Java, C++, C] } } |
Guava provides more flexibility and functionality over the built-in methods. It creates a new set object that is truly immutable. Even if the contained elements are mutable, it will not cause the Collection’s contents to appear to change. Guava also provides an ImmutableSortedSet class with similar methods returning an immutable “sorted” set containing the elements sorted by their natural ordering. However, Guava is an external library that will result in some overhead. The ImmutableSet class also does not allow null elements and it will throw a NullPointerException if any of its elements is null.
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)