Java : Map の初期化方法いろいろ
本記事では、Map の初期値を設定する方法を4つご紹介します。
その中で、インスタンスイニシャライザはあまりおすすめしない理由も補足します。
マップの初期化
簡単なまとめ
初期化方法 | おすすめ度 | 不変オブジェクト |
---|---|---|
Map.of | ☆☆☆ | 〇 |
Map.ofEntries | ☆☆☆ | 〇 |
マップのコンストラクタ | ☆☆ | × |
インスタンスイニシャライザ | ☆ | × |
関連 : 不変オブジェクト(イミュータブル) とは
コード例
初期化方法 | コード例 |
---|---|
Map.of |
|
Map.ofEntries |
|
マップのコンストラクタ |
|
インスタンスイニシャライザ |
|
Map.of
もし 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 を変更しようとすると
- 非チェック例外 : UnsupportedOperationException
が発生します。
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
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 に変換してから指定するのがポイントですね。
不変オブジェクトなので、オブジェクトを変更するメソッド(put や remove など) は使えません。
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 発生
マップのコンストラクタ
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 などのコンストラクタ
を使うのがおすすめです。
関連記事
- List(リスト)の基本
- Map(マップ)の基本
- Set(セット)の基本
- List の初期化方法いろいろ
- Set の初期化方法いろいろ
- 配列 vs. List
- 配列からListへの変換
- List から配列への変換
- API 使用例
- Collection (コレクション)
- Collections (コレクション操作)
- Comparable
- Comparator
- Iterator
- List (リスト)
- Map (マップ)
- Map.Entry (キーと値のペア)
- Queue (キュー)
- AbstractQueue
- ArrayBlockingQueue (ブロッキング・配列キュー)
- ArrayDeque (両端キュー)
- BlockingDeque (ブロッキング・両端キュー)
- BlockingQueue (ブロッキング・キュー)
- ConcurrentLinkedDeque (並列処理用・両端キュー)
- ConcurrentLinkedQueue (並列処理用キュー)
- Deque (両端キュー)
- LinkedBlockingDeque (ブロッキング・リンク両端キュー)
- LinkedBlockingQueue (ブロッキング・リンクキュー)
- LinkedList (二重リンク・リスト)
- PriorityBlockingQueue (ブロッキング・優先度付きキュー)
- PriorityQueue (優先度付きキュー)
- RandomAccess
- Set (セット)
- Spliterator