如何通过构造初始化HashSet值?

我需要创建一个初始值Set

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

有没有一种方法可以在一行代码中做到这一点?例如,它对最终静态字段很有用。

1012174 次浏览

收集文字计划在7Java,但没有进入。所以还没有自动。

您可以使用番石榴Sets

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

或者您可以使用以下语法,这将创建一个匿名类,但它很老土:

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

有几种方法:

双括号初始化

这是一种创建匿名内部类的技术,该类具有一个实例初始化器,该初始化器在创建实例时将String添加到自身:

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

请记住,这实际上会在每次使用HashSet时创建一个新的子类,即使不必显式编写新的子类。

实用方法

编写一个返回Set并使用所需元素初始化的方法并不难编写:

public static Set<String> newHashSet(String... strings) {HashSet<String> set = new HashSet<String>();
for (String s : strings) {set.add(s);}return set;}

上面的代码只允许使用String,但允许使用泛型使用任何类型应该不会太难。

使用图书馆

许多库都有一个方便的方法来初始化集合对象。

例如,谷歌收藏有一个Sets.newHashSet(T...)方法,它将用特定类型的元素填充HashSet

你可以在Java6:

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

但是为什么呢?我不认为它比显式添加元素更具可读性。

我使用的一个速记不是很省时,但只适用于一行:

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

同样,这不是时间效率,因为您正在构建一个数组,转换为一个列表并使用该列表创建一个集合。

初始化静态最终集时,我通常这样写:

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

稍微不那么丑陋,效率对静态初始化无关紧要。

有点复杂,但从Java5工作:

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

使用helper方法使其可读:

Set<String> h = asSet ("a", "b");
public Set<String> asSet(String... values) {return new HashSet<String>(java.util.Arrays.asList(values));}

用于创建新HashSetCoobird的回答是实用函数的泛化:

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

在Java8中,我将使用:

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

这为您提供了一个使用“a”和“b”预初始化的可变Set。请注意,虽然在JDK 8中这确实返回了HashSet,但规范并不能保证它,这可能会在将来发生变化。如果你特别想要HashSet,请改为这样做:

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

我觉得最易读的是简单地使用googleguava

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

它是可变的。

这是一个优雅的解决方案:

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

如果你只有一个值并且想要得到一个不可变集,这就足够了:

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

只是一个小注意,不管你最终使用了这里提到的哪种好方法,如果这是一个通常未经修改的默认值(就像你正在创建的库中的默认设置),遵循这个模式是个好主意:

// 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 modifiableprivate final static Set<String> DEFAULT_VALUES = Collections.unmodifiableSet(...);private Set<String> values = DEFAULT_VALUES;

好处取决于您为该类创建的实例数量以及更改默认值的可能性。

如果你决定遵循这种模式,那么你还可以选择最具可读性的集合初始化方法。由于不同方法之间效率的微小差异可能无关紧要,因为你只会初始化集合一次。

如果Set的包含类型是枚举,则有java构建的工厂方法(从1.5开始):

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

对于Eclipse集合,有几种不同的方法可以在一条语句中初始化包含字符'a'和'b'的Set。Eclipse集合具有对象和原始类型的容器,因此我说明了如何使用Set<String>CharSet以及两者的可变、不可变、同步和不可修改版本。

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();

注意:我是Eclipse集合的提交者。

如果您正在寻找初始化集合的最紧凑方式,那么那就是Java9

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

使用Java10(不可修改集)

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

在这里,收集器实际上将返回Java9中引入的不可修改集,如源代码中的语句set -> (Set<T>)Set.of(set.toArray())所示。

有一点要注意是方法Collections.unmodifiableSet()返回指定集合的不可修改视图(根据留档)。不可修改视图集合是不可修改的集合,也是后备集合的视图。请注意,对后备集合可能的更改仍然是可能的,如果发生更改,它们可以通过不可修改视图看到。但是方法Collectors.toUnmodifiableSet()返回Java10中的真正不变


使用Java9(不可修改集)

以下是初始化集合的最紧凑方式:

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

我们有这个便利工厂方法的12个重载版本:

static <E> Set<E> of()

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

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

//……等等

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

那么一个自然的问题是为什么我们需要重载的版本,当我们有var-args?答案是:每个var-arg方法都在内部创建一个数组,并且具有重载版本将避免不必要的对象创建,并且还将节省我们的垃圾回收机制开销。


使用Java8(可修改集)

在Java8中使用

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

使用Java8(不可修改集)

使用Collections.unmodifiableSet()

我们可以使用Collections.unmodifiableSet()作为:

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

但它看起来有点尴尬,我们可以这样写我们自己的收藏家:

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

然后将其用作:

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

使用Collectors.collectingAnd然后()

另一种方法是使用方法Collectors.collectingAndThen(),它允许我们执行额外的整理转换:

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

如果我们只关心Set,那么我们也可以使用Collectors.toSet()代替Collectors.toCollection(HashSet::new)

也检查这个答案为Java8。

最方便的方法之一是使用泛型Collections.add全部()方法,它需要一个集合和varargs:

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

可以使用静态块进行初始化:

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

使用Java9,您可以执行以下操作:

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

您将获得一个包含元素的不可变Set。有关详细信息,请参阅Oracle留档界面设置

import com.google.common.collect.Sets;Sets.newHashSet("a", "b");

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

(丑陋)没有副作用的双括号初始化:

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

但在某些情况下,如果我们提到这是一个很好的气味,使最终集合不可变,它可能真的很有用:

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

随着便利工厂方法的发布,这可以以更清晰的方式实现:

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

Builder模式可能在这里有用。今天我遇到了同样的问题。我需要Set突变操作来返回Set对象的引用,所以我可以将其传递给超类构造函数,以便它们也可以继续添加到同一个集合中,依次从子类刚刚构建的Set构建一个新的StringSetBuilder。我写的构建器类看起来像这样(在我的例子中,它是外部类的静态内部类,但它也可以是自己的独立类):

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;}
@Overridepublic Set<String> build() {return set;}}

注意addAll()add()方法,它们是Set返回Set.add()Set.addAll()的对应项。最后注意build()方法,它返回对构建器封装的Set的引用。下面说明了如何使用此Set构建器:

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

使用Java8,我们可以将HashSet创建为:

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

如果我们想要不可修改的集合,我们可以创建一个实用方法:

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

此方法可用作:

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

Arrays.asList变体相比,将michaelberdyshev的答案与泛型相结合,并将构造函数与初始化容量相结合:

  import java.util.Collections;import java.util.HashSet;import java.util.Set;
@SafeVarargspublic static <T> Set<T> buildSetModif(final T... values) {final Set<T> modifiableSet = new HashSet<T>(values.length);Collections.addAll(modifiableSet, values);return modifiableSet;}
@SafeVarargspublic static <T> Set<T> buildSetModifTypeSafe(final T... values) {return new HashSet<T>(Arrays.asList(values));}
@SafeVarargspublic 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}
  • 如果你为init传递一些值,这很好,对于任何大的东西使用其他方法
  • 如果您不小心将类型与buildSetModif混合,则结果T将是? extends Object,这可能不是你想要的,这不能发生在buildSetModifTypeSafe变体中,这意味着buildSetModifTypeSafe(1, 2, "a");不会编译

您也可以使用vavr

import io.vavr.collection.HashSet;
HashSet.of("a", "b").toJavaSet();