How to initialize HashSet values by construction?

There is a shorthand that I use that is not very time efficient, but fits on a single line:

Set<String> h = new HashSet<>(Arrays.asList("a", "b"));

Again, this is not time efficient since you are constructing an array, converting to a list and using that list to create a set.

When initializing static final sets I usually write it like this:

public static final String[] SET_VALUES = new String[] { "a", "b" };
public static final Set<String> MY_SET = new HashSet<>(Arrays.asList(SET_VALUES));

Slightly less ugly and efficiency does not matter for the static initialization.


Collection literals were scheduled for Java 7, but didn't make it in. So nothing automatic yet.

You can use guava's Sets:

Sets.newHashSet("a", "b", "c")

Or you can use the following syntax, which will create an anonymous class, but it's hacky:

Set<String> h = new HashSet<String>() {{
    add("a");
    add("b");
}};

If you are looking for the most compact way of initializing a set then that was in Java9:

Set<String> strSet = Set.of("Apple", "Ball", "Cat", "Dog");

===================== Detailed answer below ==========================

Using Java 10 (Unmodifiable Sets)

Set<String> strSet1 = Stream.of("A", "B", "C", "D")
         .collect(Collectors.toUnmodifiableSet());

Here the collector would actually return the unmodifiable set introduced in Java 9 as evident from the statement set -> (Set<T>)Set.of(set.toArray()) in the source code.

One point to note is that the method Collections.unmodifiableSet() returns an unmodifiable view of the specified set (as per documentation). An unmodifiable view collection is a collection that is unmodifiable and is also a view onto a backing collection. Note that changes to the backing collection might still be possible, and if they occur, they are visible through the unmodifiable view. But the method Collectors.toUnmodifiableSet() returns truly immutable set in Java 10.


Using Java 9 (Unmodifiable Sets)

The following is the most compact way of initializing a set:

Set<String> strSet6 = Set.of("Apple", "Ball", "Cat", "Dog");

We have 12 overloaded versions of this convenience factory method:

static <E> Set<E> of()

static <E> Set<E> of(E e1)

static <E> Set<E> of(E e1, E e2)

// ....and so on

static <E> Set<E> of(E... elems)

Then a natural question is why we need overloaded versions when we have var-args? The answer is: every var-arg method creates an array internally and having the overloaded versions would avoid unnecessary creation of object and will also save us from the garbage collection overhead.


Using Java 8 (Modifiable Sets)

Using Stream in Java 8.

Set<String> strSet1 = Stream.of("A", "B", "C", "D")
         .collect(Collectors.toCollection(HashSet::new));

// stream from an array (String[] stringArray)
Set<String> strSet2 = Arrays.stream(stringArray)
         .collect(Collectors.toCollection(HashSet::new));

// stream from a list (List<String> stringList)
Set<String> strSet3 = stringList.stream()
         .collect(Collectors.toCollection(HashSet::new));

Using Java 8 (Unmodifiable Sets)

Using Collections.unmodifiableSet()

We can use Collections.unmodifiableSet() as:

Set<String> strSet4 = Collections.unmodifiableSet(strSet1);

But it looks slightly awkward and we can write our own collector like this:

class ImmutableCollector {
    public static <T> Collector<T, Set<T>, Set<T>> toImmutableSet() {
        return Collector.of(HashSet::new, Set::add, (l, r) -> {
            l.addAll(r);
            return l;
        }, Collections::unmodifiablSet);
    }
}

And then use it as:

Set<String> strSet4 = Stream.of("A", "B", "C", "D")
             .collect(ImmutableCollector.toImmutableSet());

Using Collectors.collectingAndThen()

Another approach is to use the method Collectors.collectingAndThen() which lets us perform additional finishing transformations:

import static java.util.stream.Collectors.*;
Set<String> strSet5 = Stream.of("A", "B", "C", "D").collect(collectingAndThen(
   toCollection(HashSet::new),Collections::unmodifiableSet));

If we only care about Set then we can also use Collectors.toSet() in place of Collectors.toCollection(HashSet::new).

Also check this answer for Java 8.