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.
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.
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");
}};
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.
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.
With the release of java9 and the convenience factory methods this is possible in a cleaner way:
Set set = Set.of("a", "b", "c");
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));
}
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());
}
}
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
}
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