我需要创建一个具有初始值的集合。

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

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


当前回答

如果你正在寻找最紧凑的初始化集合的方法,那么在Java9中:

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

===================== 下面详细的回答 ==========================

使用Java 10(不可修改的集合)

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

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

需要注意的一点是Collections.unmodifiableSet()方法返回指定集合的不可修改视图(根据文档)。不可修改的视图集合是不可修改的集合,也是备份集合上的视图。注意,对备份集合的更改仍然是可能的,如果发生更改,则通过不可修改的视图可见。但是在Java 10中,collections . tounmodifiableset()方法返回的是真正不可变的集合。


使用Java 9(不可修改的集合)

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

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

这个方便工厂方法有12个重载版本:

static <E> Set<E> of() static <E> Set<E> of(E e1) Set<E> of(E e1, E e2) / /……等等 static <E> Set<E> of(E…elem)

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


使用Java 8(可修改的集合)

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

使用Java 8(不可修改的集合)

使用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.collectingAndThen ()

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

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

如果我们只关心Set,那么我们也可以使用collections . toset()来代替collections . tocollection (HashSet::new)。

还可以检查Java 8的这个答案。

其他回答

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

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

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

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

Builder模式在这里可能有用。今天我也遇到了同样的问题。在这里,我需要Set变异操作来返回Set对象的引用,因此我可以将它传递给超类构造函数,这样它们也可以通过依次构造一个新的StringSetBuilder来继续添加到相同的Set,脱离子类刚刚构建的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()的Set返回对应物。最后注意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());
    }
}

集合字面值计划在Java 7中使用,但最终没有加入。所以还没有自动的东西。

你可以使用番石榴的套装:

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

或者你可以使用下面的语法,这将创建一个匿名类,但它是hack:

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

结合Michael Berdyshev的回答和泛型,并使用initialCapacity构造函数,与数组进行比较。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将 是什么?扩展对象,这可能不是你想要的,这不能发生在buildSetModifTypeSafe变体,这意味着buildSetModifTypeSafe(1,2, "a");不会编译