Set初期化は「重複を許さない集まりの“性質”を決める」作業
Setは「同じ要素を二度入れない」コレクションです。
だからこそ、初期化の段階で「順序は必要か」「変更できるか」「固定の集合か」を決めておくことが大事になります。
業務コードでよくあるのは、
「重複チェックのためにSetを使いたい」
「固定のコード一覧をSetで持ちたい」
「順序はどうでもいいけど、とにかく重複を排除したい」
といった場面です。
ここをきちんと整理しておくと、Setの初期化が“ただの new”ではなく、
意図の見える設計になります。
一番基本:可変の空Setを作る(new HashSet<>())
「あとで add していく前提」の標準形
業務で一番よく使うのは、「最初は空だけど、あとで要素を追加していくSet」です。
この場合は、素直に new HashSet<>() を使うのが基本です。
import java.util.HashSet;
import java.util.Set;
public class SetInitBasic {
public static void main(String[] args) {
Set<String> codes = new HashSet<>();
codes.add("A");
codes.add("B");
codes.add("A"); // 重複は無視される
System.out.println(codes); // [A, B] など(順序は保証されない)
}
}
Javaここで押さえておきたい重要ポイントは二つです。
一つ目は、「同じ値を add しても一つにまとまる」という Set の性質です。
重複を気にせず add していけるので、「一意な集合」を作るのに向いています。
二つ目は、「変数の型は Set、実体は HashSet」という書き方にしていることです。Set<String> codes = new HashSet<>(); のように、
インターフェース型(Set)で受けておくと、
あとで実装を LinkedHashSet や TreeSet に変えたくなったときも、
呼び出し側のコードをほとんど変えずに済みます。
「最初から中身が決まっているSet」を作る
Set.of で不変の集合を手軽に作る
「最初から要素が決まっていて、すぐに使いたい」という場面も多いです。
例えば、「許可されたロール一覧」「固定のフラグ値」などです。
Java 9以降なら、Set.of を使うと簡潔に書けます。
import java.util.Set;
public class SetInitFixed {
public static void main(String[] args) {
Set<String> roles = Set.of("ADMIN", "USER", "GUEST");
System.out.println(roles.contains("ADMIN")); // true
System.out.println(roles.contains("OP")); // false
}
}
Javaここでの重要ポイントは、「このSetは完全に不変(変更不可)」ということです。
roles.add("OP"); // UnsupportedOperationException
roles.remove("USER"); // UnsupportedOperationException
Java業務で「定数的な集合」「絶対に変わってほしくない許可一覧」を扱うときは、Set.of で不変Setにしておくと安心です。
どこかのコードがうっかり add しても例外になり、
「いつの間にか許可一覧が変わっていた」という事故を防げます。
「可変Setが欲しいけど、初期値も入れたい」場合の定番パターン
不変Setを元にして、可変Setを作る
よくあるのが、
「最初からいくつかの要素を入れておきたいが、その後も add/remove したい」
というパターンです。
この場合は、「不変Setを元にして、可変Setを作る」という書き方が定番です。
import java.util.HashSet;
import java.util.Set;
public class SetInitMutable {
public static void main(String[] args) {
Set<String> base = Set.of("A", "B");
Set<String> set = new HashSet<>(base);
set.add("C");
System.out.println(set); // [A, B, C] など
}
}
Javaここでの重要ポイントは、「外から見える型は Set、実体は HashSet」というスタイルを守りつつ、
「初期値入りの可変Set」を安全に作っていることです。
順序を保ちたいときは LinkedHashSet で初期化する
「登録した順番でループしたい」要件に応える
HashSet は順序を保証しません。
「画面に表示する順番を固定したい」「設定ファイルの順番をそのまま使いたい」といった要件がある場合は、LinkedHashSet を使って初期化します。
import java.util.LinkedHashSet;
import java.util.Set;
public class SetInitOrdered {
public static void main(String[] args) {
Set<String> ordered = new LinkedHashSet<>();
ordered.add("一番目");
ordered.add("二番目");
ordered.add("三番目");
for (String s : ordered) {
System.out.println(s);
}
// 一番目
// 二番目
// 三番目
}
}
Javaここでの重要ポイントは、「順序を保つかどうかも“初期化の時点で決まる”」ということです。
後から「やっぱり順番が必要だった」と気づいても、
Setの実装を変えるのは影響が大きくなりがちです。
最初に「このSetは順序が意味を持つか?」を考えておく癖をつけると、設計が安定します。
null を返さない Set初期化ユーティリティという発想
「空のSetを返す」ことをルールにするとコードが楽になる
ListやMapと同じく、Setでもよくある悪いパターンが
「該当がなければ null を返す」というメソッドです。
// 悪い例
Set<String> findTags() {
if (条件に合うものがなければ) {
return null;
}
...
}
Javaこれをやると、呼び出し側は毎回こう書く必要が出てきます。
Set<String> tags = findTags();
if (tags != null && tags.contains("VIP")) {
...
}
Javanull チェックの書き忘れがバグの温床になります。
そこで、「Setを返すメソッドは、要素がなくても必ず空のSetを返す」というルールにしてしまうと、
呼び出し側がとても楽になります。
import java.util.Collections;
import java.util.Set;
public final class SetUtils {
private SetUtils() {}
public static <T> Set<T> emptyIfNull(Set<T> set) {
return (set == null) ? Collections.emptySet() : set;
}
}
Java使い方はこうです。
Set<String> tags = SetUtils.emptyIfNull(findTags());
if (tags.contains("VIP")) {
...
}
Javaここでの重要ポイントは、「null を“空のSet”に正規化する」という発想です。
これにより、「nullかもしれないSet」と「空のSet」を区別しなくてよくなり、
コードの分岐が減ります。
まとめ:Set初期化で本当に意識してほしいこと
Set初期化は、単なる new HashSet<>() や Set.of(...) の話ではなく、
「この集合は変わるのか、変わらないのか」「順序は意味を持つのか」「nullを返すのか、空を返すのか」
といった設計の話です。
可変Setが欲しいなら new HashSet<>() を基本形にする。
定数的な集合なら Set.of(...) で不変Setにしておく。
初期値入りの可変Setが欲しいなら new HashSet<>(Set.of(...)) のようにコピーする。
順序が大事なら最初から LinkedHashSet で初期化する。
メソッドの戻り値としては「nullではなく空のSetを返す」ルールにしておく。
もしあなたのコードのどこかに、if (set == null) { ... } のようなnullチェックが何度も出てきているなら、
それを「Set初期化とnull正規化のユーティリティ」で置き換えられないか、一度眺めてみてください。
その小さな意識の変化が、
「コレクションの性質を理解して、安定した業務コードを書けるエンジニア」への、
確かな一歩になります。
