ChatGPT解决这个技术问题 Extra ChatGPT

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

我需要创建一个具有初始值的 Set

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

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

对于 Java 8、Java 9 和 Java 10,请查看我的答案 stackoverflow.com/a/37406054/1216775
Michael Berdyshev 的回答实际上是最好的,因为它是生成可修改集的最干净的方法。 i_am_zero 答案也有不同的方式来制作可修改的 Set,但它更冗长/笨拙[使用 Lambda 流];否则 i_am_zero 答案是次佳的,因为它的不同选项的广度(跨 Java 版本)。
注意:有些答案省略了`new HashSet(int initialCapacity) 参数,如果您已经知道集合的大小,请使用它。

G
Gennadiy

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

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

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


尽管在撰写本文时很可能是正确的,但从 Java 7 开始确实使用 Diamond 运算符,如下所示: Set h = new HashSet<>(Arrays.asList("a", "b")); public static final Set MY_SET = new HashSet<>(Arrays.asList(SET_VALUES));
@Gennadiy 可以确认菱形符号可以正常工作(即使在 Set 声明和 HashSet 分配中也是如此。)
@NestorMilyaev,Gennadiy,stream.of(...) 怎么样?使用 java 8 是一个更好的解决方案吗?
在 Java 9 中:Set<String> h = Set.of("a", "b") //Unmodifiable This answer is up to date
i
ihebiheb

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

您可以使用番石榴的 Sets

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

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

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

@DD 这是一个 hack,因为它创建了一个匿名的 HashSet 内部子类,其中包含一个调用 add() 方法的静态初始化部分,这对于这种情况来说绝对是矫枉过正。我喜欢它!
双括号初始化是一个非常危险的技巧。匿名类具有到外部类的隐式链接,这可能导致内存泄漏。持有双括号初始化集合的人也持有创建该集合的类。
@fhucho 该方法在(比如)JMock 中设置模拟对象期望值时非常流行,即使考虑到内存泄漏风险(测试是短暂的进程,其内存占用并不重要),它在这种情况下也是有意义的。
a
akhil_mittal

如果您正在寻找最紧凑的初始化集合的方法,那就是 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() 返回指定集合的不可修改视图(根据文档)。 不可修改的视图 集合是不可修改的集合,也是支持集合的视图。请注意,对支持集合的更改可能仍然是可能的,如果它们发生,它们通过不可修改的视图可见。但是方法 Collectors.toUnmodifiableSet() 返回 真正不可变Java 10 中设置。

使用 Java 9(不可修改集)

以下是初始化集合的最简洁方式:

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

我们有这个 convenience factory 方法的 12 个重载版本:

static Set of() static Set of(E e1) static Set of(E e1, E e2) // ....等等 static 设置 of(E...elems)

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

使用 Java 8(可修改集)

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

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

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

另请检查 Java 8 的 this 答案。


欢呼“为什么我们有 var-args 时需要重载版本?”部分
Set.of() 的未来读者添加警告。如果您尝试添加重复元素,则会抛出 IllegalArgumentException
2
2 revs, 2 users 57%

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

这比原始问题中的普通旧代码更加冗长和不必要的复杂。
@Natix 对于 2 个元素,它更冗长,但是如果您必须用更多元素(10-100 秒)对集合进行硬编码,这种表示开始变得简洁
@Natix 是的,但它是内联的,在某些情况下这是一个优势
为什么不是更紧凑的版本: Stream.of(1, 3, 5).collect(toSet());它使用 import static java.util.stream.Collectors.toSet;
此外,如果您想要一个只有一项的 Set,可以创建一个 Singletonfinal Set<T> SOME_SET = Collections.singleton(t);
B
Bohemian

有几种方法:

双括号初始化

这是一种创建匿名内部类的技术,该内部类具有实例初始化程序,该初始化程序在创建实例时将 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,但允许使用任何使用泛型的类型应该不会太难。

使用图书馆

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

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


GoogleCollections 也有 ImmutableSet.of(T...)。大多数情况下,您不需要在以后再次更改集合,在这种情况下,使用不可变集合有很多优点。
澄清一下,每次使用它都会创建一个新的 HashSet 子类,意思是“每次编码”。每次执行都不会创建新的子类。
如果您不知道自己在做什么,双括号启动可能很危险,请在使用前查看其含义
M
Michael Berdyshev

最方便的方法之一是使用通用 Collections.addAll() 方法,该方法采用集合和可变参数:

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

谢谢!很高兴我进一步阅读,imo这应该排名更高。这对于 Java 8 及以下版本非常有用;没有复制数组和笨拙的流。谢谢!
这是一个很好的答案,因为与许多其他答案不同,据我所知,它提供了一个可修改的 Set ;)
我认为这至少在 Java 5+ 中可用。
我刚刚未能通过编码测试,因为我花了很多时间试图弄清楚如何从文字创建 HashSet。感谢这个金块 - 我将来会使用它!
I
Ilya Serbis

如果您只有一个值并且想要获得一个不可变的集合,这就足够了:

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

这非常有用,因为如果您使用只有一个值的 new HashSet<>(Arrays.asList( _values_ )) 方法,我们的代码分析工具会报错。
请注意,生成的 Set 是不可变的。
M
Mathias Bader

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

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

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


S
Satish Patro

我觉得最易读的就是简单地使用google Guava:

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

它是可变的。


现在是 Sets.newHashSet()
@membersound 我应该从哪个包导入以使用 Sets.newHashSet() ?
@AbdennacerLachiheb“com.google.common.collect.Sets”
@membersound 我检查了旧文档直到 guava 2.0,没有 newSet(),我猜 LanceP 提到了其他一些框架,比如 gs-collections。所以,按照你说的修改答案。谢谢
J
Jason Nichols

你可以在 Java 6 中做到这一点:

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

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


我在声明中创建它。这是最终财产。谢谢
啊。可能值得注意的是,您仍然可以将元素添加到最终集合中。只需声明 set final,但像往常一样在构造函数(或其他任何地方)中添加元素。最终的 Collection 仍然可以添加或删除元素。如果是最终的,则只有对 Collection 本身的引用。
+1 杰森尼科尔斯所说的话。不要忘记使用 Collections.unmodifiableSet(...)。
我的意思是,如果它是new HashSet<String>("a", "b", "c");,它会更具可读性
H
Heri

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

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

C
Community

用于创建新 HashSetcoobird's answer's 效用函数的概括:

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

请用大小初始化 + 添加空检查
A
Avery Michelle Dawn
import com.google.common.collect.Sets;
Sets.newHashSet("a", "b");

或者

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

D
Donald Raab

对于 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 的提交者。


N
Naman

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

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

@cellepo好吧,该答案的编辑历史表明它是后来编辑的。尽管同意内容是相同的,并且该答案涵盖了更多解决问题的方法。
A
Amr Mostafa

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

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

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

如果您决定遵循这种模式,那么您还可以选择最易读的集合初始化方法。由于不同方法之间效率上的微小差异可能并不重要,因为您只需初始化一次集合。


e
egorlitvinenko

(丑陋)无副作用的双大括号初始化:

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

A
Aaron Digulla

有点复杂,但适用于 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));
}

(您不需要 new String[] {
a
akhil_mittal

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

A
Ariel

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

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

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

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 都是一个非常糟糕的主意
J
Jose Quijada

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

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

请注意 addAll()add() 方法,它们是 Set 返回 Set.add()Set.addAll() 的对应项。最后注意 build() 方法,它返回对构建器封装的 Set 的引用。下面说明了如何使用这个 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

Michael Berdyshev 的答案与泛型相结合,并使用带有 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 将是?扩展对象,这可能不是您想要的,这不会发生在 buildSetModifTypeSafe 变体中,这意味着 buildSetModifTypeSafe(1, 2, "a");不会编译


D
Def_Os

您还可以使用 vavr

import io.vavr.collection.HashSet;

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