広告

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

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


マップの初期化

簡単なまとめ

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

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


コード例

初期化方法 コード例
Map.of
final Map<String, Integer> map = Map.of(
        "a", 10,
        "b", 20,
        "c", 30);
System.out.println(map); // {a=10, b=20, c=30}
Map.ofEntries
final Map<String, Integer> map = Map.ofEntries(
        Map.entry("a", 10),
        Map.entry("b", 20),
        Map.entry("c", 30));
System.out.println(map); // {a=10, b=20, c=30}
マップのコンストラクタ
final Map<String, Integer> map = new HashMap<>(Map.of(
        "a", 10,
        "b", 20,
        "c", 30));
System.out.println(map); // {a=10, b=20, c=30}
インスタンスイニシャライザ
final Map<String, Integer> map = new HashMap<>() {
    {
        put("a", 10);
        put("b", 20);
        put("c", 30);
    }
};
System.out.println(map); // {a=10, b=20, c=30}

Map.of

static <K, V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3)
3つのマッピングを含む変更不可能なマップを返します。

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

final Map<String, Integer> map = Map.of(
        "a", 10,
        "b", 20,
        "c", 30);
System.out.println(map); // {a=10, b=20, c=30}

メソッドのパラメータに キーと値を交互に 指定していくのがポイントですね。

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

が発生します。

Map にキーと値を追加する put メソッドを使う例です。

final Map<String, Integer> map = Map.of(
        "a", 10,
        "b", 20,
        "c", 30);
System.out.println(map); // {a=10, b=20, c=30}

map.put("d", 40); // UnsupportedOperationException 発生

補足

  • Map.of メソッドは、キーと値のペアを10個までしか指定できません。
    もし11個以上指定したい場合は、次にご紹介する Map.ofEntries メソッドが使えます。

Map.ofEntries

static <K, V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries)
指定されたエントリから抽出されたキーと値を含む変更不可能なマップを返します。

Map.ofEntries メソッドは 不変オブジェクト である Map を作成します。
また、Map.of メソッドと違い、11個以上のキーと値のペアを指定できます。

final Map<String, Integer> map = Map.ofEntries(
        Map.entry("a", 10),
        Map.entry("b", 20),
        Map.entry("c", 30));
System.out.println(map); // {a=10, b=20, c=30}

キーと値のペアを Map.Entry に変換してから指定するのがポイントですね。

不変オブジェクトなので、オブジェクトを変更するメソッド(putremove など) は使えません。

final Map<String, Integer> map = Map.ofEntries(
        Map.entry("a", 10),
        Map.entry("b", 20),
        Map.entry("c", 30));
System.out.println(map); // {a=10, b=20, c=30}

map.put("d", 40); // UnsupportedOperationException 発生

マップのコンストラクタ

public HashMap(Map<? extends K,? extends V> m)
指定されたコレクションの要素が含まれているリストを、コレクションのイテレータによって返される順序で構築します。

HashMap クラスは Map インタフェースの実装の1つです。
コンストラクタに Map を指定すことでマップを初期化できます。

クラス図

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

  • Map.of メソッド
  • Map.ofEntries メソッド

を組み合わせてマップを初期化できます。

もちろん、HashMap なのでマップの変更 (追加・削除) が可能です。

【 Map.of を使った例 】

final Map<String, Integer> map = new HashMap<>(Map.of(
        "a", 10,
        "b", 20,
        "c", 30));
System.out.println(map); // {a=10, b=20, c=30}

map.put("d", 40);
System.out.println(map); // {a=10, b=20, c=30, d=40}

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

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

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

final Map<String, Integer> map = new HashMap<>() {
    {
        put("a", 10);
        put("b", 20);
        put("c", 30);
    }
};
System.out.println(map); // {a=10, b=20, c=30}

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

final Map<String, Integer> map = new HashMap<>() {
    ... 匿名クラスの宣言 ...
};

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

final Map<String, Integer> map = new HashMap<>() {
    {
        ... インスタンスイニシャライザ ...
    }
};

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

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

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

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

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

まとめ

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

  • 不変オブジェクトなら … Map.of or Map.ofEntries メソッド
  • 可変オブジェクトなら … HashMap などのコンストラクタ

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


関連記事

ページの先頭へ