初期値でSet
を作成する必要があります。
Set<String> h = new HashSet<String>();
h.add("a");
h.add("b");
これを1行のコードで実行する方法はありますか?たとえば、最後の静的フィールドに役立ちます。
私が使用している速記はそれほど時間的に効率的ではありませんが、単一行に収まります。
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用にスケジュールされていましたが、うまくいきませんでした。だから自動は何もしていません。
Guavaの Sets
を使うことができます。
Sets.newHashSet("a", "b", "c")
あるいは、次のような構文を使用して匿名クラスを作成することもできますが、それは厄介です。
Set<String> h = new HashSet<String>() {{
add("a");
add("b");
}};
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));
Set<String> strSet1 = Stream.of("A", "B", "C", "D")
.collect(Collectors.toUnmodifiableSet());
ここでコレクターは実際にはソースコードのステートメントset -> (Set<T>)Set.of(set.toArray())
から明らかなようにJava 9で導入された変更不可能なセットを返すでしょう。
Set<String> strSet6 = Set.of("Apple", "Ball", "Cat", "Dog");
この コンビニエンスファクトリ methodには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で 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));
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 - もう1つの方法は、追加の仕上げ変換を実行できるメソッド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)
を使うこともできます。
注意すべき点は、メソッドCollections::unmodifiableSet
が、 doc のように、指定されたセットの変更不可能なビューを返すことです。変更不可能なビューコレクションは、変更不可能なコレクションであり、バッキングコレクションに対するビューでもあります。バッキングコレクションへの変更はまだ可能である可能性があり、変更が加えられても変更不可能なビューを通して表示されますが、メソッドCollectors.unmodifiableSet
はJava 10)で本当に不変のセットを返します。
いくつかの方法があります。
二重括弧の初期化
これは、インスタンスが作成されたときに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
に特定の型の要素を追加します。
Setに初期値が1つしかない場合は、これで十分です。
Set<String> h = Collections.singleton("a");
あなたはJava 6でそれをすることができます:
Set<String> h = new HashSet<String>(Arrays.asList("a", "b", "c"));
しかし、なぜ?要素を明示的に追加するよりも読みやすくなるとは思いません。
最も便利な方法の1つは、コレクションと可変引数をとるgeneric Collections.addAll() methodの使用です。
Set<String> h = new HashSet<String>();
Collections.addAll(h, "a", "b");
私が最も読みやすいのは単にグーグルグアバを使用することであると感じます:
Set<String> StringSet = Sets.newSet("a", "b", "c");
新しいHashSet
sを作成するための coobird's answer's utility関数の一般化:
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, ... );
Eclipseのコレクション を使って、1つのステートメントに文字 '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コレクションのコミッターです。
import com.google.common.collect.Sets;
Sets.newHashSet("a", "b");
または
import com.google.common.collect.ImmutableSet;
ImmutableSet.of("a", "b");
副作用のない(醜い)ダブルブレースの初期化:
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));
}
ちょっと注意してください、ここで述べたどの良いアプローチにもかかわらず、これが通常変更されないデフォルトである場合(作成しているライブラリのデフォルト設定のように)、このパターンに従うのは良い考えです。 :
// 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;
その利点は、そのクラスに作成したインスタンスの数と、デフォルトがどの程度変更されるかによって異なります。
このパターンに従うことにした場合は、最も読みやすい集合初期化の方法を選ぶことになります。あなたが一度だけセットを初期化することになるので、異なる方法の間の効率の微妙な違いはおそらくあまり問題にならないでしょう。
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));
Java9 および コンビニエンスファクトリメソッド のリリースにより、これはよりクリーンな方法で可能になります。
Set set = Set.of("a", "b", "c");
初期化に静的ブロックを使用できます。
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);
}
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
}
buildSetModif
と混合すると、結果のTは? extends Object
になりますが、これはおそらく望んでいないものです。これはbuildSetModifTypeSafe
バリアントでは起こり得ません。つまり、buildSetModifTypeSafe(1, 2, "a");
はコンパイルされませんこれはエレガントな解決策です。
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の変更操作が必要な場合建てられました。私が書いたビルダークラスは次のようになります(私の場合は外部クラスの静的な内部クラスですが、それ自身の独立したクラスにすることもできます)
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;
}
}
SetがaddAll()
とadd()
の対応するものを返すSetであるSet.add()
とSet.addAll()
メソッドに注目してください。最後に、ビルダーがカプセル化したSetへの参照を返すbuild()
メソッドに注目してください。次に、この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());
}
}