1. 概要

スレッド間で読み取り専用データを共有するなど、java.util.Mapへの変更を許可しない方が望ましい場合があります。 この目的のために、変更不可能なマップまたは不変のマップのいずれかを使用できます。

このクイックチュートリアルでは、それらの違いを確認します。 次に、不変マップを作成するためのさまざまな方法を紹介します。

2. 変更不可vs不変

変更不可能なマップは、変更可能なマップの単なるラッパーであり、直接変更することはできません。

Map<String, String> mutableMap = new HashMap<>();
mutableMap.put("USA", "North America");

Map<String, String> unmodifiableMap = Collections.unmodifiableMap(mutableMap);
assertThrows(UnsupportedOperationException.class,
  () -> unmodifiableMap.put("Canada", "North America"));

ただし、基になる可変マップは引き続き変更でき、変更は変更不可マップにも反映されます。

mutableMap.remove("USA");
assertFalse(unmodifiableMap.containsKey("USA"));
		
mutableMap.put("Mexico", "North America");
assertTrue(unmodifiableMap.containsKey("Mexico"));

一方、不変マップには独自のプライベートデータが含まれており、変更することはできません。したがって、不変マップのインスタンスが作成されると、データを変更することはできません。

3. グアバの不変マップ

Guava は、ImmutableMapを使用して各java.utilMapの不変バージョンを提供します。 変更しようとすると、UnsupportedOperationExceptionがスローされます。

独自のプライベートデータが含まれているため、元のマップが変更されてもこのデータは変更されません。

次に、ImmutableMapのインスタンスを作成するさまざまな方法について説明します。

3.1. copyOf()メソッドの使用

まず、元のマップと同じようにすべてのエントリのコピーを返す ImmutableMap.copyOf()メソッドを使用しましょう。

ImmutableMap<String, String> immutableMap = ImmutableMap.copyOf(mutableMap);
assertTrue(immutableMap.containsKey("USA"));

直接的または間接的に変更することはできません。

assertThrows(UnsupportedOperationException.class,
  () -> immutableMap.put("Canada", "North America"));
		
mutableMap.remove("USA");
assertTrue(immutableMap.containsKey("USA"));
		
mutableMap.put("Mexico", "North America");
assertFalse(immutableMap.containsKey("Mexico"));

3.2. builder()メソッドの使用

ImmutableMap.builder()メソッドを使用して、元のマップと同じようにすべてのエントリのコピーを作成することもできます。

さらに、このメソッドを使用して、元のマップに存在しないエントリを追加できます。

ImmutableMap<String, String> immutableMap = ImmutableMap.<String, String>builder()
  .putAll(mutableMap)
  .put("Costa Rica", "North America")
  .build();
assertTrue(immutableMap.containsKey("USA"));
assertTrue(immutableMap.containsKey("Costa Rica"));

前の例と同じように、直接または間接的に変更することはできません。

assertThrows(UnsupportedOperationException.class,
  () -> immutableMap.put("Canada", "North America"));
		
mutableMap.remove("USA");
assertTrue(immutableMap.containsKey("USA"));
		
mutableMap.put("Mexico", "North America");
assertFalse(immutableMap.containsKey("Mexico"));

3.3. of()メソッドの使用

最後に、 ImmutableMap.of()メソッドを使用して、その場で提供される一連のエントリを使用して不変のマップを作成できます。 最大5つのキー/値のペアをサポートします。

ImmutableMap<String, String> immutableMap
  = ImmutableMap.of("USA", "North America", "Costa Rica", "North America");
assertTrue(immutableMap.containsKey("USA"));
assertTrue(immutableMap.containsKey("Costa Rica"));

変更することもできません。

assertThrows(UnsupportedOperationException.class,
  () -> immutableMap.put("Canada", "North America"));

4. 結論

この簡単な記事では、変更不可能なマップと不変のマップの違いについて説明しました。

また、GuavaのImmutableMapを作成するさまざまな方法も検討しました。

そして、いつものように、完全なコード例はGitHub利用できます。