ChatGPT解决这个技术问题 Extra ChatGPT

How to initialize HashSet values by construction?

I need to create a Set with initial values.

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

Is there a way to do this in one line of code? For instance, it's useful for a final static field.

For Java 8, Java 9 and Java 10 check my answer stackoverflow.com/a/37406054/1216775
Answer by Michael Berdyshev is actually the best, because it is the cleanest way to produce a modifiable Set. i_am_zero Answer also has a different way to make a modifiable Set, but it is more verbose/clumsy [using Lambda streaming]; otherwise i_am_zero Answer is next-best for the breadth of its different options (across Java versions).
NOTE: Some answers omit `new HashSet(int initialCapacity) parameter, if you already know the size of the collection use it.

G
Gennadiy

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.


Although most probably correct at the time of writing, as of Java 7 do use the Diamond operator as in: Set h = new HashSet<>(Arrays.asList("a", "b")); public static final Set MY_SET = new HashSet<>(Arrays.asList(SET_VALUES));
@Gennadiy Can confirm the diamond notation works fine (even across the Set declaration and HashSet assignment.)
@NestorMilyaev, Gennadiy how about stream.of(...)? is that a better solution with java 8 around?
In Java 9: Set<String> h = Set.of("a", "b") //Unmodifiable This answer is up to date
i
ihebiheb

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");
}};

@DD it's a hack because it's creating an anonymous inner subclass of HashSet with a static initialisation section that calls the add() method, which is definitely overkill for this situation. I love it!
The double brace initialization is a very dangerous hack. The anonymous class has an implicit link to the outer class, which could lead to memory leaks. Whoever holds the double-brace-initialized set also holds the class where the set was created.
@fhucho The method is very popular for setting mock object Expectations in (say) JMock, and it makes sense in that case even in light of the memory leak risk (tests are short-lived processes whose memory footprint isn't important).
a
akhil_mittal

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 Set of() static Set of(E e1) static Set of(E e1, E e2) // ....and so on static Set 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.


cheers for the "why we need overloaded versions when we have var-args?" section
Adding an caveat for the future readers for Set.of(). If you are trying to add duplicates elements, it will throw an IllegalArgumentException
2
2 revs, 2 users 57%

In Java 8 I would use:

Set<String> set = Stream.of("a", "b").collect(Collectors.toSet());

This gives you a mutable Set pre-initialized with "a" and "b". Note that while in JDK 8 this does return a HashSet, the specification doesn't guarantee it, and this might change in the future. If you specifically want a HashSet, do this instead:

Set<String> set = Stream.of("a", "b")
                        .collect(Collectors.toCollection(HashSet::new));

This is even more verbose and needlessly complex than the plain old code in the original question.
@Natix For 2 elements it is more verbose, but this representation starts becoming concise if you have to hard code the set with more elements(10-100s)
@Natix Yes, But it is inline, in some cases that's is a advantage
Why not the more compact version: Stream.of(1, 3, 5).collect(toSet()); It uses import static java.util.stream.Collectors.toSet;
ALso, if you want a Set with only one item, one could create a Singleton: final Set<T> SOME_SET = Collections.singleton(t);
B
Bohemian

There are a few ways:

Double brace initialization

This is a technique which creates an anonymous inner class which has an instance initializer which adds Strings to itself when an instance is created:

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

Keep in mind that this will actually create an new subclass of HashSet each time it is used, even though one does not have to explicitly write a new subclass.

A utility method

Writing a method that returns a Set which is initialized with the desired elements isn't too hard to write:

public static Set<String> newHashSet(String... strings) {
    HashSet<String> set = new HashSet<String>();

    for (String s : strings) {
        set.add(s);
    }
    return set;
}

The above code only allows for a use of a String, but it shouldn't be too difficult to allow the use of any type using generics.

Use a library

Many libraries have a convenience method to initialize collections objects.

For example, Google Collections has a Sets.newHashSet(T...) method which will populate a HashSet with elements of a specific type.


GoogleCollections also has ImmutableSet.of(T...). Most of the time when doing this you don't need to change the set again later, and in that case, using an immutable set has many advantages.
To clarify will create an new subclass of HashSet each time it is used, means "each time it is coded". Each execution does not create a new subclass.
Double brace initiation can be dangerous if you don't know what you're doing, look up the implications before using
M
Michael Berdyshev

One of the most convenient ways is usage of generic Collections.addAll() method, which takes a collection and varargs:

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

Thank you! Glad I read down further, imo this should be ranked higher. This is great for Java 8 and less; no copying around arrays and clumsy streams. Thanks!
This a great Answer, because unlike many other answers, as far as I can tell, it gives a modifiable Set ;)
I think this is available in at least Java 5+.
I just now failed a Coding test because I spent a lot of time trying to figure out how to create a HashSet from literals. Thanks for this nugget - I will use it in the future!
I
Ilya Serbis

If you have only one value and want to get an immutable set this would be enough:

Set<String> immutableSet = Collections.singleton("a");

This was very useful because our code analysis tool complains if you use the new HashSet<>(Arrays.asList( _values_ )) method with only a single value.
Beware, the resulting Set is immutable.
M
Mathias Bader

With Java 9 you can do the following:

Set.of("a", "b");

and you'll get an immutable Set containing the elements. For details see the Oracle documentation of interface Set.


S
Satish Patro

I feel the most readable is to simply use google Guava:

Set<String> StringSet = Sets.newHashSet("a", "b", "c");

It's mutable.


This is now Sets.newHashSet().
@membersound from which package should I import to use Sets.newHashSet() ?
@AbdennacerLachiheb "com.google.common.collect.Sets"
@membersound I checked old docs till guava 2.0, there is no newSet(), I guess LanceP referred to some other framework like gs-collections. So, modified the answer as per you said. Thanks
J
Jason Nichols

You can do it in Java 6:

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

But why? I don't find it to be more readable than explicitly adding elements.


I create it in declaration. It's final property. Thanks
Ah. It's probably worth noting you can still add elements to a final set. Simply declare the set final, but add elements in the constructor (or anywhere else) as you normally would. A final Collection can still have elements added or removed. Only the reference to the Collection itself if final.
+1 to what Jason Nichols said. Don't forget to use Collections.unmodifiableSet(...).
i mean, it would be more readable if it were new HashSet<String>("a", "b", "c");
H
Heri

If the contained type of the Set is an enumeration then there is java built factory method (since 1.5):

Set<MY_ENUM> MY_SET = EnumSet.of( MY_ENUM.value1, MY_ENUM.value2, ... );

C
Community

A generalization of coobird's answer's utility function for creating new HashSets:

public static <T> Set<T> newHashSet(T... objs) {
    Set<T> set = new HashSet<T>();
    for (T o : objs) {
        set.add(o);
    }
    return set;
}

init with size please + add null check
A
Avery Michelle Dawn
import com.google.common.collect.Sets;
Sets.newHashSet("a", "b");

or

import com.google.common.collect.ImmutableSet;
ImmutableSet.of("a", "b");

D
Donald Raab

With Eclipse Collections there are a few different ways to initialize a Set containing the characters 'a' and 'b' in one statement. Eclipse Collections has containers for both object and primitive types, so I illustrated how you could use a Set<String> or CharSet in addition to mutable, immutable, synchronized and unmodifiable versions of both.

Set<String> set =
    Sets.mutable.with("a", "b");
HashSet<String> hashSet =
    Sets.mutable.with("a", "b").asLazy().into(new HashSet<String>());
Set<String> synchronizedSet =
    Sets.mutable.with("a", "b").asSynchronized();
Set<String> unmodifiableSet =
    Sets.mutable.with("a", "b").asUnmodifiable();

MutableSet<String> mutableSet =
    Sets.mutable.with("a", "b");
MutableSet<String> synchronizedMutableSet =
    Sets.mutable.with("a", "b").asSynchronized();
MutableSet<String> unmodifiableMutableSet =
    Sets.mutable.with("a", "b").asUnmodifiable();

ImmutableSet<String> immutableSet =
    Sets.immutable.with("a", "b");
ImmutableSet<String> immutableSet2 =
    Sets.mutable.with("a", "b").toImmutable();

CharSet charSet =
    CharSets.mutable.with('a', 'b');
CharSet synchronizedCharSet =
    CharSets.mutable.with('a', 'b').asSynchronized();
CharSet unmodifiableCharSet =
    CharSets.mutable.with('a', 'b').asUnmodifiable();
MutableCharSet mutableCharSet =
    CharSets.mutable.with('a', 'b');
ImmutableCharSet immutableCharSet =
    CharSets.immutable.with('a', 'b');
ImmutableCharSet immutableCharSet2 =
    CharSets.mutable.with('a', 'b').toImmutable();

Note: I am a committer for Eclipse Collections.


N
Naman

With the release of and the convenience factory methods this is possible in a cleaner way:

Set set = Set.of("a", "b", "c");

@cellepo Well, the edit history of that answer suggests it was edited later. Though agreed the content is the same and that answer covers a lot more ways to solve the question.
A
Amr Mostafa

Just a small note, regardless of which of the fine approaches mentioned here you end up with, if this is a default that usually goes unmodified (like a default setting in a library you are creating), it is a good idea to follow this pattern:

// Initialize default values with the method you prefer, even in a static block
// It's a good idea to make sure these defaults aren't modifiable
private final static Set<String> DEFAULT_VALUES = Collections.unmodifiableSet(...);
private Set<String> values = DEFAULT_VALUES;

The benefit depends on the number of instances you create of that class and how likely it's that defaults will be changed.

If you decide to follow this pattern, then you also get to pick the method of set initialization that's most readable. As the micro differences in efficiency between the different methods will probably not matter much as you will be initializing the set only once.


e
egorlitvinenko

(ugly) Double Brace Initialization without side effects:

Set<String> a = new HashSet<>(new HashSet<String>() {{
    add("1");
    add("2");
}})

But in some cases, if we mentioned that is a good smell to make final collections unmutable, it could be really useful:

final Set<String> a = Collections.unmodifiableSet(new HashSet<String>(){{
    add("1");
    add("2");
}})

A
Aaron Digulla

A bit convoluted but works from Java 5:

Set<String> h = new HashSet<String>(Arrays.asList(new String[] {  
    "a", "b"
}))

Use a helper method to make it readable:

Set<String> h = asSet ("a", "b");

public Set<String> asSet(String... values) {
    return new HashSet<String>(java.util.Arrays.asList(values));
}

(You didn't need the new String[] {)
a
akhil_mittal

Using Java 8 we can create HashSet as:

Stream.of("A", "B", "C", "D").collect(Collectors.toCollection(HashSet::new));

And if we want unmodifiable set we can create a utility method as :

public static <T, A extends Set<T>> Collector<T, A, Set<T>> toImmutableSet(Supplier<A> supplier) {
        return Collector.of(
                supplier,
                Set::add, (left, right) -> {
                    left.addAll(right);
                    return left;
                }, Collections::unmodifiableSet);
    }

This method can be used as :

 Stream.of("A", "B", "C", "D").collect(toImmutableSet(HashSet::new));

A
Ariel

Can use static block for initialization:

private static Set<Integer> codes1=
        new HashSet<Integer>(Arrays.asList(1, 2, 3, 4));

private static Set<Integer> codes2 =
        new HashSet<Integer>(Arrays.asList(5, 6, 7, 8));

private static Set<Integer> h = new HashSet<Integer>();

static{
    h.add(codes1);
    h.add(codes2);
}

T
ToxiCore

This is an elegant solution:

public static final <T> Set<T> makeSet(@SuppressWarnings("unchecked") T... o) {
        return new HashSet<T>() {
            private static final long serialVersionUID = -3634958843858172518L;
            {
                for (T x : o)
                   add(x);
            }
        };
}

serialVersionUID is an incredibly bad idea in all cases
J
Jose Quijada

The Builder pattern might be of use here. Today I had the same issue. where I needed Set mutating operations to return me a reference of the Set object, so I can pass it to super class constructor so that they too can continue adding to same set by in turn constructing a new StringSetBuilder off of the Set that the child class just built. The builder class I wrote looks like this (in my case it's a static inner class of an outer class, but it can be its own independent class as well):

public interface Builder<T> {
    T build();
}

static class StringSetBuilder implements Builder<Set<String>> {
    private final Set<String> set = new HashSet<>();

    StringSetBuilder add(String pStr) {
        set.add(pStr);
        return this;
    }

    StringSetBuilder addAll(Set<String> pSet) {
        set.addAll(pSet);
        return this;
    }

    @Override
    public Set<String> build() {
        return set;
    }
}

Notice the addAll() and add() methods, which are Set returning counterparts of Set.add() and Set.addAll(). Finally notice the build() method, which returns a reference to the Set that the builder encapsulates. Below illustrates then how to use this Set builder:

class SomeChildClass extends ParentClass {
    public SomeChildClass(String pStr) {
        super(new StringSetBuilder().add(pStr).build());
    }
}

class ParentClass {
    public ParentClass(Set<String> pSet) {
        super(new StringSetBuilder().addAll(pSet).add("my own str").build());
    }
}

C
Christophe Roussy

Combining answer by Michael Berdyshev with Generics and using constructor with initialCapacity, comparing with Arrays.asList variant:

  import java.util.Collections;
  import java.util.HashSet;
  import java.util.Set;

  @SafeVarargs
  public static <T> Set<T> buildSetModif(final T... values) {
    final Set<T> modifiableSet = new HashSet<T>(values.length);
    Collections.addAll(modifiableSet, values);
    return modifiableSet;
  }

  @SafeVarargs
  public static <T> Set<T> buildSetModifTypeSafe(final T... values) {
    return new HashSet<T>(Arrays.asList(values));
  }

  @SafeVarargs
  public static <T> Set<T> buildeSetUnmodif(final T... values) {
    return Collections.unmodifiableSet(buildSetModifTypeSafe(values));
    // Or use Set.of("a", "b", "c") if you use Java 9
  }

This is good if you pass a few values for init, for anything large use other methods

If you accidentally mix types with buildSetModif the resulting T will be ? extends Object, which is probably not what you want, this cannot happen with the buildSetModifTypeSafe variant, meaning that buildSetModifTypeSafe(1, 2, "a"); will not compile


D
Def_Os

You can also use vavr:

import io.vavr.collection.HashSet;

HashSet.of("a", "b").toJavaSet();