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

问题描述 投票:645回答:23

我需要用初始值创建一个Set

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

有没有办法在一行代码中执行此操作?例如,它对最终的静态字段很有用。

java collections constructor initialization hashset
23个回答
849
投票

我使用的速记不是非常节省时间,但适用于单行:

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

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


22
投票

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

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

你会得到一个包含元素的不可变集合。有关详细信息,请参阅Oracle documentation of interface Set


14
投票

用于创建新coobird's answer'ss的HashSet效用函数的推广:

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

12
投票

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

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

5
投票

使用Eclipse Collections,有一些不同的方法可以在一个语句中初始化包含字符'a'和'b'的Set。 Eclipse Collections具有对象和基本类型的容器,因此我说明了如何使用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 Collections与Java 5 - 8兼容。

注意:我是Eclipse Collections的提交者。


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

要么

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

4
投票

(丑陋)Double Brace Initialization没有副作用:

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

3
投票

有点复杂,但从Java 5开始:

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

使用辅助方法使其可读:

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

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

3
投票

只是一个小小的注释,无论你最终提到哪种精细方法,如果这是一个通常未经修改的默认设置(如你正在创建的库中的默认设置),那么遵循这种模式是个好主意:

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

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

如果您决定遵循此模式,那么您还可以选择最易读的集初始化方法。由于不同方法之间效率的微观差异可能无关紧要,因为您只需初始化一次。


3
投票

使用Java 8,我们可以创建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));

2
投票

随着convenience factory methods的发布,这可以更清洁的方式:

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

336
投票

集合文字是为Java 7安排的,但没有进入。所以没有自动化。

你可以使用番石榴的Sets

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

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

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

1
投票

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

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

0
投票

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

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

0
投票

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

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

注意addAll()add()方法,它们是返回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());
    }
}

0
投票

将Michael Berdyshev的答案与Generics结合使用并将构造函数与initialCapacity结合使用,与Arrays.asList变体进行比较:

  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
  }
  • 如果为init传递一些值,这对于任何大型使用其他方法都是好的
  • 如果你不小心将类型与buildSetModif混合,结果T将是? extends Object,这可能不是你想要的,这不会发生在buildSetModifTypeSafe变体,这意味着buildSetModifTypeSafe(1, 2, "a");将无法编译

179
投票

在Java 8中我会使用:

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

135
投票

Using Java 10 (Unmodifiable Sets)

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

这里的收集器实际上会返回Java 9中引入的不可修改的集合,这可以从源代码中的语句set -> (Set<T>)Set.of(set.toArray())中看出。

需要注意的一点是,方法Collections.unmodifiableSet()返回指定集的不可修改的视图(根据文档)。不可修改的视图集合是一个不可修改的集合,也是一个支持集合的视图。请注意,可能仍然可以对支持集合进行更改,如果它们发生,则通过不可修改的视图可以看到它们。但是方法Collectors.toUnmodifiableSet()在Java 10中返回真正的不可变集。


Using Java 9 (Unmodifiable Sets)

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

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

我们有这个convenience factory方法的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方法在内部创建一个数组,并且具有重载版本将避免不必要的对象创建,并且还将使我们免于垃圾收集开销。


Using Java 8 (Modifiable Sets)

在Java 8中使用Stream

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

我们可以使用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());

Using Collectors.collectingAndThen()

另一种方法是使用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)


86
投票

有几种方法:

双支撑初始化

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

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,但允许使用泛型的任何类型都不应该太难。

使用图书馆

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

例如,Google Collections有一个Sets.newHashSet(T...)方法,它将使用特定类型的元素填充HashSet


35
投票

如果你只有一个值并想获得一个不可变的集合,那就足够了:

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

27
投票

你可以用Java 6做到这一点:

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

但为什么?我没有发现它比显式添加元素更具可读性。


26
投票

最方便的方法之一是使用通用的Collections.addAll()方法,它采用集合和变量:

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

24
投票

我觉得最可读的是简单地使用谷歌番石榴:

Set<String> StringSet = Sets.newSet("a", "b", "c");
© www.soinside.com 2019 - 2024. All rights reserved.