Initialize Set in Java
This post will discuss various methods to initialize a set in Java in a single line.
Java is often criticized for its verbosity. For example, creating a set containing n elements involves constructing it, storing it in a variable, invoking the add() method on it n times, and then maybe wrapping it to make it unmodifiable:
|
1 2 3 4 5 6 7 |
Set<Integer> set = new HashSet<>(); set.add(1); set.add(2); set.add(3); set = Collections.unmodifiableSet(set); |
This post will discuss various methods to initialize a set in a single expression.
1. Constructor
We can initialize a set by using HashSet constructor that can accept another collection, as shown below:
|
1 |
Set<Integer> mutableSet = new HashSet<>(Arrays.asList(1, 2, 3)); |
Any duplicate elements present in the list will be rejected. This approach is not time efficient as we’re initially creating an array, converting it to a list, and passing that list to create a set.
2. Java Collections
The Collections class consists of several static methods that operate on collections and return a new collection backed by a specified collection. Please note that duplicate elements will be rejected.
⮚ Collections.addAll()
Collections.addAll() adds all the specified elements to the specified collection. Elements to be added may be specified individually or as an array. When elements are specified individually, this method provides a convenient way to add a few elements to an existing collection:
|
1 2 |
Set<Integer> mutableSet = Collections.EMPTY_SET; Collections.addAll(mutableSet = new HashSet<Integer>(), 1, 2, 3); |
⮚ Collections.unmodifiableSet()
Alternatively, one can populate a collection using a copy constructor from another collection. One such method is Collections.unmodifiableSet() returns an unmodifiable view of the specified set. Any attempts to modify the returned set will result in an UnsupportedOperationException.
|
1 2 |
Set<Integer> unmodifiableSet = Collections.unmodifiableSet( new HashSet<>(Arrays.asList(1, 2, 3))); |
Collections also have an unmodifiableSortedSet(SortedSet s) method that returns an unmodifiable view of the specified sorted set.
⮚ Collections.singleton()
If we want a set containing only a single element, we can use Collections.singleton() that returns an immutable set containing that element. The set will throw an UnsupportedOperationException if any modification operation is performed on it.
|
1 |
Set<Integer> set = Collections.singleton(1); |
3. Double Brace Initialization
Another alternative is to use “Double Brace Initialization”. This creates an anonymous inner class with just an instance initializer in it.
|
1 2 3 4 5 |
Set<Integer> set = new HashSet<Integer>() {{ add(1); add(2); add(3); }}; |
This creates a new class that inherits from HashSet. We should avoid this approach as it costs an extra class at each usage. It also holds hidden references to the enclosing instance and any captured objects. This may cause memory leaks or problems with serialization.
4. Using Java 8
We can use the Java 8 Stream to construct small sets by obtaining stream from static factory methods and accumulating the input elements into a new set using collectors. For example,
⮚ Collectors.toSet()
Collectors.toSet() returns a Collector that accumulates the input elements into a new Set.
|
1 2 |
Set<Integer> set = Stream.of(1, 2, 3) .collect(Collectors.toSet()); |
⮚ Collectors.toCollection()
The specification doesn’t guarantee the type of set returned by toSet(). Even though Java 8 and above returns a HashSet, this might change in future releases. We can use toCollection() to ensure that the returned type of Set is HashSet, as shown below:
|
1 2 |
Set<Integer> set = Stream.of(1, 2, 3) .collect(Collectors.toCollection(HashSet::new)); |
⮚ Collectors.collectingAndThen()
We could adapt a collector to perform an additional finishing transformation. For example, we could adapt the toSet() collector to always produce an unmodifiable set with:
|
1 2 3 |
Set<Integer> set = Stream.of(1, 2, 3) .collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::<Integer>unmodifiableSet)); |
5. Java 9
Java 9 made it convenient to create instances of a list with small numbers of elements by providing Sets.of() static factory methods that create compact, unmodifiable instances. For example,
|
1 |
Set<String> unmodifiableSet = Set.of("C", "C++", "Java"); |
Java 9 provides 12 overloaded versions of this method with one varargs method, which can handle any number of elements.
static <E> Set<E> of()
static <E> Set<E> of(E e1)1
static <E> Set<E> of(E e1, E e2)
……
……
static <E> Set<E> of(E e1, E e2, E e3, E e4, . . . E e8, E e9)
static <E> Set<E> of(E e1, E e2, E e3, E e4, . . . E e8, E e9, E e10)
static <E> Set<E> of(E… elements)
The obvious question is why Java 9 has included so many extra methods when only var-args can suffice? The reason is there’s a subtle runtime performance advantage. The var-args version is likely to run slower than the overloadings that do not use varargs. This is because every invocation of a varargs method will cause an array allocation and initialization and, not to forget, GC overhead.
As per Javadoc, the set instances created by Set.of() have the following characteristics:
- They are structurally immutable. Elements cannot be added, removed, or replaced. Calling any mutator method will always cause
UnsupportedOperationExceptionto be thrown. However, if the contained elements are themselves mutable, this may cause the set’s contents to appear to change. - They disallow null elements. Attempts to create them with null elements result in
NullPointerException. - They are serializable if all elements are serializable.
- They reject duplicate elements at creation time. Duplicate elements passed to a static factory method result in
IllegalArgumentException. - The order of elements in the list is the same as the order of the provided arguments or of the elements in the provided array.
- They are value-based. Callers should make no assumptions about the identity of the returned instances. Factories are free to create new instances or reuse existing ones. Therefore, identity-sensitive operations on these instances (reference equality (==), identity hash code, and synchronization) are unreliable and should be avoided.
If we need a set that can expand or shrink, we can use:
|
1 |
Set<String> list = new ArraySet<>(Set.of("C", "C++", "Java")); |
Please note that unlike static methods on classes, static methods on interfaces are not inherited, so it will not be possible to invoke them via an implementing class nor an instance of the interface type.
6. Using Apache Commons Collections
Apache Commons Collections SetUtils class provides unmodifiableSet() that returns an unmodifiable set backed by the given set. It throws a NullPointerException if the given set is null and an UnsupportedOperationException if any modification operation is performed on it.
|
1 2 |
Set<Integer> unmodifiableSet = SetUtils.unmodifiableSet( new HashSet<>(Arrays.asList(1, 2, 3)); |
7. Using Guava Library
Guava also provides several static utility methods pertaining to set instances. Refer to the following post for details:
That’s all about initializing Set in Java.
Reference: JEP 269: Convenience Factory Methods for Collections
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 :)