広告

Java : Set の初期化方法いろいろ

本記事では、Set の初期値を設定する方法を4つご紹介します。
その中で、インスタンスイニシャライザはあまりおすすめしない理由も補足します。


セットの初期化

簡単なまとめ

初期化方法 おすすめ度 不変オブジェクト
Set.of ☆☆☆
セットのコンストラクタ ☆☆ ×
Collections.addAll ☆☆ ×
インスタンスイニシャライザ ×

関連 : 不変オブジェクト(イミュータブル) とは


コード例

初期化方法 コード例
Set.of
final Set<String> set = Set.of("aaa", "bbb", "ccc");
System.out.println(set); // [aaa, bbb, ccc]
セットのコンストラクタ
final Set<String> set = new HashSet<>(Set.of("aaa", "bbb", "ccc"));
System.out.println(set); // [aaa, ccc, bbb]
Collections.addAll
final Set<String> set = new HashSet<>();
Collections.addAll(set, "aaa", "bbb", "ccc");
System.out.println(set); // [aaa, ccc, bbb]
インスタンスイニシャライザ
final Set<String> set = new HashSet<>() {
    {
        add("aaa");
        add("bbb");
        add("ccc");
    }
};
System.out.println(set); // [aaa, ccc, bbb]

Set.of

static <E> Set<E> of(E... elements)任意の数の要素を含む変更不可能な集合を返します。

もし Set に対して変更が必要なければ、Set.of メソッドを使うのがおすすめです。
Set.of メソッドは、不変オブジェクト である Set を作成します。

final Set<String> set = Set.of("aaa", "bbb", "ccc");
System.out.println(set); // [aaa, bbb, ccc]

作成した Set は不変オブジェクトです。
そのため、Set を変更しようとすると

が発生します。

Set に要素を追加する add メソッドを使う例です。

final Set<String> set = Set.of("aaa", "bbb", "ccc");
System.out.println(set); // [aaa, bbb, ccc]

set.add("XXX"); // UnsupportedOperationException 発生

セットのコンストラクタ

public HashSet(Collection<? extends E> c)
指定されたコレクションの要素を格納する新規セットを作成します。

HashSet クラスは Set インタフェースの実装の1つです。
コンストラクタに Collection を指定すことで要素を初期化できます。

(ちなみに Set は Collection のサブインタフェースです)

クラス図

少し冗長かもしれませんが、HashSet のコンストラクタと

  • Set.of メソッド

を組み合わせて Set を初期化できます。

もちろん、HashSet なのでセットの変更 (追加・削除) が可能です。

final Set<String> set = new HashSet<>(Set.of("aaa", "bbb", "ccc"));
System.out.println(set); // [aaa, ccc, bbb]

set.add("XXX");
System.out.println(set); // [aaa, ccc, bbb, XXX]

※今回は HashSet を使いましたが、他の実装 … 例えば LinkedHashSet を使っても OK です。

Collections.addAll

public static <T> boolean addAll(Collection<? super T> c, T... elements)
指定されたすべての要素を指定されたコレクションに追加します。

もし1行の初期化にこだわらないのであれば、Collections.addAll メソッドが使えます。

final Set<String> set = new HashSet<>();
Collections.addAll(set, "aaa", "bbb", "ccc");
System.out.println(set); // [aaa, ccc, bbb]

もちろん、HashSet なのでセットの変更 (追加・削除) が可能です。

final Set<String> set = new HashSet<>();
Collections.addAll(set, "aaa", "bbb", "ccc");
System.out.println(set); // [aaa, ccc, bbb]

set.add("XXX");
System.out.println(set); // [aaa, ccc, bbb, XXX]

インスタンスイニシャライザ

インスタンスイニシャライザを使った初期化も可能です。
具体的な例で見てみましょう。

final Set<String> set = new HashSet<>() {
    {
        add("aaa");
        add("bbb");
        add("ccc");
    }
};
System.out.println(set); // [aaa, ccc, bbb]

これは、分解すると2つの構造に分けられます。
最初の { ... } は匿名クラス(Anonymous Class) の宣言です。

final Set<String> set = new HashSet<>() {
    ... 匿名クラスの宣言 ...
};

そして、その中の { ... } がインスタンスイニシャライザです。

final Set<String> set = new HashSet<>() {
    {
        ... インスタンスイニシャライザ ...
    }
};

インスタンスイニシャライザはさておき、匿名クラスには注意点があります。

  • 匿名クラスは、内部クラスの1つです。そのため、外側のクラス参照を暗黙的に保持します。
    • これはガベージ・コレクタによるメモリ解放を阻害するおそれがあります。

  • HashSet は serialVersionUID を定義していますが、その匿名クラスは serialVersionUID を定義していません。
    • シリアライズするときは特に注意が必要です。
    • 静的解析ツール (lint など) で「serialVersionUID を定義していませんよ」と警告されることがあります。
    • 外側のクラス参照を持つので、意図せず外側のクラスまでシリアライズしてしまうリスクがあります。

もし使うのであれば、匿名クラスとインスタンスイニシャライザをよく理解してから使うことをおすすめします。

参考 : [Java] 匿名クラスとインスタンスイニシャライザを使って初期化したデータのシリアライズに失敗した話

まとめ

本記事では、Set の初期化方法を4つご紹介しました。

  • 不変オブジェクトなら … Set.of メソッド
  • 可変オブジェクトなら … セットのコンストラクタ or Collections.addAll メソッド

を使うのがおすすめです。


関連記事

ページの先頭へ