我需要Set
用初始值创建一个。
Set<String> h = new HashSet<String>();
h.add("a");
h.add("b");
有没有办法在一行代码中做到这一点?例如,它对于最终的静态字段很有用。
我需要Set
用初始值创建一个。
Set<String> h = new HashSet<String>();
h.add("a");
h.add("b");
有没有办法在一行代码中做到这一点?例如,它对于最终的静态字段很有用。
我使用的速记不是很省时,但适合一行:
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 安排的,但没有进入。所以还没有自动的。
您可以使用番石榴的Sets
:
Sets.newHashSet("a", "b", "c")
或者您可以使用以下语法,这将创建一个匿名类,但它很 hacky:
Set<String> h = new HashSet<String>() {{
add("a");
add("b");
}};
如果您正在寻找最紧凑的初始化集合的方法,那么就是Java9
:
Set<String> strSet = Set.of("Apple", "Ball", "Cat", "Dog");
=====================下面详细解答==========================
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中真正不可变的集合。
以下是初始化集合的最简洁方式:
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 方法在内部创建一个数组,重载版本可以避免不必要的对象创建,也可以让我们避免垃圾收集开销。
在 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));
我们可以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()
允许我们执行额外的精加工转换的方法:
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 的这个答案。
在 Java 8 中,我会使用:
Set<String> set = Stream.of("a", "b").collect(Collectors.toSet());
这为您提供了一个Set
用“a”和“b”预初始化的可变变量。请注意,虽然在 JDK 8 中这确实返回 a HashSet
,但规范并不能保证它,而且这可能会在未来发生变化。如果您特别想要 a HashSet
,请改为执行以下操作:
Set<String> set = Stream.of("a", "b")
.collect(Collectors.toCollection(HashSet::new));
有几种方法:
双括号初始化
这是一种创建匿名内部类的技术,该内部类具有实例初始化程序,该初始化程序String
在创建实例时将 s 添加到自身:
Set<String> s = new HashSet<String>() {{
add("a");
add("b");
}}
请记住,这实际上会在HashSet
每次使用它时创建一个新的子类,即使您不必显式地编写一个新的子类。
一种实用方法
编写一个返回Set
用所需元素初始化的 a 的方法并不难编写:
public static Set<String> newHashSet(String... strings) {
HashSet<String> set = new HashSet<String>();
for (String s : strings) {
set.add(s);
}
return set;
}
上面的代码只允许使用 a String
,但允许使用任何使用泛型的类型应该不会太难。
使用图书馆
许多库都有一个方便的方法来初始化集合对象。
例如,Google Collections有一个Sets.newHashSet(T...)
方法可以HashSet
用特定类型的元素填充 a。
最方便的方法之一是使用泛型Collections.addAll()方法,它接受一个集合和可变参数:
Set<String> h = new HashSet<String>();
Collections.addAll(h, "a", "b");
如果您只有一个值并且想要获得一个不可变的集合,这就足够了:
Set<String> immutableSet = Collections.singleton("a");
你可以在 Java 6 中做到这一点:
Set<String> h = new HashSet<String>(Arrays.asList("a", "b", "c"));
但为什么?我不觉得它比显式添加元素更具可读性。
我觉得最易读的是简单地使用google Guava:
Set<String> StringSet = Sets.newHashSet("a", "b", "c");
它是可变的。
coobird 的答案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;
}
如果 Set 的包含类型是枚举,则有 java 内置工厂方法(从 1.5 开始):
Set<MY_ENUM> MY_SET = EnumSet.of( MY_ENUM.value1, MY_ENUM.value2, ... );
import com.google.common.collect.Sets;
Sets.newHashSet("a", "b");
或者
import com.google.common.collect.ImmutableSet;
ImmutableSet.of("a", "b");
使用Eclipse Collections有几种不同的方法来初始化Set
在一个语句中包含字符 'a' 和 'b' 的 a。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 的提交者。
只是一个小提示,无论您最终使用哪种方法,如果这是通常未修改的默认设置(例如您正在创建的库中的默认设置),那么遵循此模式是一个好主意:
// 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;
好处取决于您为该类创建的实例数量以及更改默认值的可能性。
如果您决定遵循这种模式,那么您还可以选择最易读的集合初始化方法。由于不同方法之间效率上的微小差异可能并不重要,因为您只需初始化一次集合。
(丑陋)无副作用的双大括号初始化:
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");
}})
有点复杂,但适用于 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));
}
使用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));
可以使用静态块进行初始化:
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);
}
这是一个优雅的解决方案:
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 对象的引用,所以我可以将它传递给超类构造函数,以便它们也可以继续添加到同一个集合中,依次从子类的 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()
andadd()
方法,它们是 Set 返回Set.add()
and的对应项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());
}
}
将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
}
buildSetModif
生成的 T will be混合在一起? extends Object
,这可能不是您想要的,则buildSetModifTypeSafe
变体不会发生这种情况,这意味着 buildSetModifTypeSafe(1, 2, "a");
不会编译您还可以使用vavr:
import io.vavr.collection.HashSet;
HashSet.of("a", "b").toJavaSet();