如何通过构造初始化HashSet值?
Posted
技术标签:
【中文标题】如何通过构造初始化HashSet值?【英文标题】:How to initialize HashSet values by construction? 【发布时间】:2011-01-03 17:57:56 【问题描述】:我需要创建一个带有初始值的Set
。
Set<String> h = new HashSet<String>();
h.add("a");
h.add("b");
有没有办法在一行代码中做到这一点?例如,它对 final 静态字段很有用。
【问题讨论】:
对于 Java 8、Java 9 和 Java 10,请查看我的答案 ***.com/a/37406054/1216775 Michael Berdyshev 的回答实际上是最好的,因为它是生成 modifiable 集的最干净的方法。 i_am_zero 答案也有不同的方式来制作可修改的 Set,但它更冗长/笨拙[使用 Lambda 流];否则 i_am_zero 答案是次佳的,因为它的不同选项的广度(跨 Java 版本)。 注意:有些答案会省略`new HashSet集合文字是为 Java 7 安排的,但没有进入。所以还没有自动的。
可以使用guava的Sets
:
Sets.newHashSet("a", "b", "c")
或者您可以使用以下语法,这将创建一个匿名类,但它很hacky:
Set<String> h = new HashSet<String>()
add("a");
add("b");
;
【讨论】:
@DD 这是一个 hack,因为它创建了一个匿名的 HashSet 内部子类,其中包含一个调用 add() 方法的静态初始化部分,这对于这种情况来说绝对是矫枉过正。我喜欢它! 双括号初始化是一种非常危险的技巧。匿名类具有到外部类的隐式链接,这可能导致内存泄漏。持有双括号初始化集合的人也持有创建该集合的类。 @fhucho 该方法在(比如)JMock 中设置模拟对象期望值时非常流行,即使考虑到内存泄漏风险(测试是短暂的进程,其内存足迹并不重要)。【参考方案2】:有几种方法:
双括号初始化
这是一种创建匿名内部类的技术,该内部类具有实例初始化程序,在创建实例时将String
s 添加到自身:
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子类,意思是“每次编码”。每个执行都不创建一个新的子类。
如果您不知道自己在做什么,双括号启动可能会很危险,请在使用前查看其含义【参考方案3】:
你可以在 Java 6 中做到这一点:
Set<String> h = new HashSet<String>(Arrays.asList("a", "b", "c"));
但是为什么呢?我不觉得它比显式添加元素更具可读性。
【讨论】:
我在声明中创建它。这是最终财产。谢谢 啊。可能值得注意的是,您仍然可以将元素添加到最终集合中。只需声明 set final,但像往常一样在构造函数(或其他任何地方)中添加元素。最终的 Collection 仍然可以添加或删除元素。如果是最终的,则仅对 Collection 本身的引用。 对 Jason Nichols 所说的话 +1。不要忘记使用 Collections.unmodifiableSet(...)。 我的意思是,如果是new HashSet<String>("a", "b", "c");
,它会更具可读性
请注意Arrays#asList returns a structurally immutable List【参考方案4】:
我使用的速记不是很省时,但适合一行:
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 运算符,如下所示: SetSet<String> h = Set.of("a", "b") //Unmodifiable
This answer is up to date
请注意Arrays#asList returns a structurally immutable List【参考方案5】:
有点复杂,但适用于 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[]
)【参考方案6】:
coobird's answer's 实用函数的泛化,用于创建新的HashSet
s:
public static <T> Set<T> newHashSet(T... objs)
Set<T> set = new HashSet<T>();
for (T o : objs)
set.add(o);
return set;
【讨论】:
请用大小初始化 + 添加空检查【参考方案7】:在 Java 8 中我会使用:
Set<String> set = Stream.of("a", "b").collect(Collectors.toSet());
这给你一个可变的Set
,用“a”和“b”预初始化。请注意,虽然在 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
,可以创建一个Singleton
:final Set<T> SOME_SET = Collections.singleton(t);
【参考方案8】:
我觉得最易读的就是简单地使用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。所以,按照你说的修改答案。谢谢【参考方案9】:
这是一个优雅的解决方案:
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 在所有情况下都是一个非常糟糕的主意【参考方案10】:如果您只有一个值并且想要获得一个 不可变 集,这就足够了:
Set<String> immutableSet = Collections.singleton("a");
【讨论】:
这非常有用,因为如果您使用只有一个值的new HashSet<>(Arrays.asList( _values_ ))
方法,我们的代码分析工具会报错。
当心,结果 Set只是一个小提示,无论您最终使用哪种方法,如果这是通常未修改的默认设置(例如您正在创建的库中的默认设置),最好遵循这个模式:
// 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;
好处取决于您为该类创建的实例数量以及更改默认值的可能性。
如果您决定遵循这种模式,那么您还可以选择最易读的集合初始化方法。由于不同方法之间效率上的微小差异可能并不重要,因为您只需初始化一次集合。
【讨论】:
【参考方案12】:如果 Set 的包含类型是枚举,则有 java 内置工厂方法(从 1.5 开始):
Set<MY_ENUM> MY_SET = EnumSet.of( MY_ENUM.value1, MY_ENUM.value2, ... );
【讨论】:
【参考方案13】:对于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 的提交者。
【讨论】:
【参考方案14】:如果您正在寻找最紧凑的初始化集合的方法,那就是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 <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 方法都会在内部创建一个数组,并且拥有重载的版本可以避免不必要的对象创建,也可以让我们避免垃圾回收的开销。
使用 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)
。
同时检查 this 对 Java 8 的回答。
【讨论】:
欢呼“为什么我们有 var-args 时需要重载版本?”部分【参考方案15】:最方便的方法之一是使用泛型Collections.addAll() 方法,它接受一个集合和可变参数:
Set<String> h = new HashSet<String>();
Collections.addAll(h, "a", "b");
【讨论】:
谢谢!很高兴我进一步阅读,imo这应该排名更高。这对于 Java 8 及以下版本非常有用;没有复制数组和笨拙的流。谢谢! 这是一个很好的答案,因为与许多其他答案不同,据我所知,它提供了一个 modifiable Set ;) 我认为这至少在 Java 5+ 中可用。【参考方案16】:可以使用静态块进行初始化:
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);
【讨论】:
【参考方案17】:使用 Java 9,您可以执行以下操作:
Set.of("a", "b");
你会得到一个包含元素的不可变集合。有关详细信息,请参阅 Oracle documentation of interface Set。
【讨论】:
【参考方案18】:import com.google.common.collect.Sets;
Sets.newHashSet("a", "b");
或
import com.google.common.collect.ImmutableSet;
ImmutableSet.of("a", "b");
【讨论】:
【参考方案19】:(丑陋)没有副作用的双括号初始化:
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");
)
【讨论】:
【参考方案20】:随着java9 和convenience factory methods 的发布,这可能以更简洁的方式实现:
Set set = Set.of("a", "b", "c");
【讨论】:
@cellepo 好吧,该答案的编辑历史表明它是后来编辑的。尽管同意内容是相同的,并且该答案涵盖了解决问题的更多方法。【参考方案21】:Builder 模式在这里可能有用。今天我遇到了同样的问题。我需要 Set 变异操作来返回 Set 对象的引用,所以我可以将它传递给超类构造函数,以便它们也可以继续添加到同一个集合中,依次从子类的 Set 中构造一个新的 StringSetBuilder刚刚建成。我写的builder类是这样的(在我的例子中它是一个外部类的静态内部类,但它也可以是它自己的独立类):
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());
【讨论】:
【参考方案22】:使用 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));
【讨论】:
【参考方案23】:将 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 将
是? extends Object
,这可能不是你想要的,buildSetModifTypeSafe
变体不会发生这种情况,这意味着buildSetModifTypeSafe(1, 2, "a");
不会编译
【讨论】:
【参考方案24】:你也可以使用vavr:
import io.vavr.collection.HashSet;
HashSet.of("a", "b").toJavaSet();
【讨论】:
以上是关于如何通过构造初始化HashSet值?的主要内容,如果未能解决你的问题,请参考以下文章