Guavaのcom.google.common.collect.Multimap
の利用サンプルです。
Multimap
は、特定のkeyに対して値を複数持つことができるデータ構造です。
業務処理などでは、このようなデータ構造が必要となるケースが少なくないと思います。非常に強力なデータ構造です。
(Java標準で提供してくれてもいいような気がするんですが・・)
Multimapを使わない場合
Multimapを使わずに特定keyに対して値を複数持つ場合、以下のようなものを定義することになると思います
Map<String, List<String>> normalMap = new HashMap<String, List<String>>();
このMapに対する操作は(極端に書けば)以下のようになると思います
Map<String, List<String>> normalMap = new HashMap<String, List<String>>(); if (!normalMap.containsKey("key1")) { normalMap.put("key1", new ArrayList<String>()); } normalMap.get("key1").add("value1"); if (!normalMap.containsKey("key1")) { normalMap.put("key1", new ArrayList<String>()); } normalMap.get("key1").add("value2"); if (!normalMap.containsKey("key1")) { normalMap.put("key1", new ArrayList<String>()); } normalMap.get("key1").add("value3"); if (!normalMap.containsKey("key2")) { normalMap.put("key2", new ArrayList<String>()); } normalMap.get("key2").add("data1"); if (!normalMap.containsKey("key3")) { normalMap.put("key3", new ArrayList<String>()); } normalMap.get("key3").add("Taro");
激しくめんどくさいです。
こんな時はMultimapに頼りましょう。 以下例は、ArrayListのようなデータ型で管理されるvalueを持つMultimapとなっています。
keyに対してvalueを追加する
とりあえずMultimapを生成して、keyに対してvalueを追加(紐づけ)します
■サンプルコード
Multimap<String, String> multiMap = ArrayListMultimap.create(); multiMap.put("key1", "value1"); multiMap.put("key1", "value2"); multiMap.put("key1", "value3"); multiMap.put("key2", "data1"); multiMap.put("key3", "Taro"); System.out.println("Multimap#toString: " + multiMap);
■実行結果
Multimap#toString: {key1=[value1, value2, value3], key2=[data1], key3=[Taro]}
非常にシンプルに書けます
各個別の要素にアクセスする
通常のMapであれば、valueのList内の各要素全てにアクセスしようとすると、結構めんどくさいです。
が、Multimapであれば以下のようにMultiMap#entries
を利用することで簡単に(全key紐づく全valueに対してフラットに)アクセスできます
■サンプルコード
for (Entry<String, String> entry : multiMap.entries()) { System.out.println("MultiMap::" + entry.getKey() + " -> " + entry.getValue()); }
■実行結果
MultiMap::key1 -> value1 MultiMap::key1 -> value2 MultiMap::key1 -> value3 MultiMap::key2 -> data1 MultiMap::key3 -> Taro
特定のkeyに複数のvalueをまとめて追加する
MultiMap#putAll
で特定のkeyに複数のvalueをまとめて追加できます
■サンプルコード
System.out.println("--- Before Multimap#toString: " + multiMap); List<String> values = Arrays.asList("data2", "data3", "data4", "data1", "data2"); multiMap.putAll("key2", values); System.out.println("--- After Multimap#toString: " + multiMap);
■実行結果
--- Before Multimap#toString: {key1=[value1, value2, value3], key2=[data1], key3=[Taro]} --- After Multimap#toString: {key1=[value1, value2, value3], key2=[data1, data2, data3, data4, data1, data2], key3=[Taro]}
特定のvalueが含まれているかを個別にチェックする
keyに紐づいたList内の要素に特定の値が含まれているかをチェックするのは、普通のMapを使っている場合には、これまた面倒です。
Multimapでは、Multimap#containsValue
でチェックできます
■サンプルコード
System.out.println("Multimap#toString: " + multiMap); // 特定のvalueが存在するかを個別にチェックできる System.out.println("Multimap#containsValue(data3) -> " + multiMap.containsValue("data3")); System.out.println("Multimap#containsValue(data999) -> " + multiMap.containsValue("data999"));
■実行結果
Multimap#toString: {key1=[value1, value2, value3], key2=[data1, data2, data3, data4, data1, data2], key3=[Taro]} Multimap#containsValue(data3) -> true Multimap#containsValue(data999) -> false
特定のkeyとvalueの組み合わせが含まれているかをチェックする
Multimapでは、Multimap#containsEntry
で特定のkeyとvalueの組み合わせのエントリが存在しているかをチェックできます
■サンプルコード
System.out.println("Multimap#toString: " + multiMap); // 特定のkeyとvalueの組み合わせが存在するかをチェックできる System.out.println("Multimap#containsEntry(key1, data3) -> " + multiMap.containsEntry("key1", "data3")); System.out.println("Multimap#containsEntry(key2, data3) -> " + multiMap.containsEntry("key2", "data3")); System.out.println("Multimap#containsEntry(key99, value1) -> " + multiMap.containsEntry("key99", "value1"));
■実行結果
Multimap#toString: {key1=[value1, value2, value3], key2=[data1, data2, data3, data4, data1, data2], key3=[Taro]} Multimap#containsEntry(key1, data3) -> false Multimap#containsEntry(key2, data3) -> true Multimap#containsEntry(key99, value1) -> false
エントリ数分のkeyを取得する
Multimap#keys
を使うことで、エントリ数文のkeyをMultisetで受け取ることができます
■サンプルコード
System.out.println("Multimap#toString: " + multiMap); // Multimap#keys で 全keyをMultisetで受け取れる(個別のvalue分のkeyが格納されている) Multiset<String> keys = multiMap.keys(); System.out.println("Multimap#keys -> " + keys);
■実行結果
Multimap#keys -> [key1 x 3, key2 x 6, key3]
特定のkeyとvalueの組み合わせのエントリを削除する
Multimap#remove
を使うことで、指定のkeyとvalueの組み合わせのエントリだけを削除することができます。
対象の組のエントリが複数存在する場合は、1エントリだけ削除されます。
■サンプルコード
System.out.println("--- Before Multimap#toString: " + multiMap); // MultiMap#remove(key, value) で 指定のkeyとvalueの組み合わせの要素だけ削除する // 存在する組み合わせなので戻り値trueが返る System.out.println("Multimap#remove(key1, value3) -> " + multiMap.remove("key1", "value3")); // 存在しない組み合わせなので戻り値falseが返る System.out.println("Multimap#remove(key2, value3) -> " + multiMap.remove("key2", "value3")); // 対象の組み合わせが複数存在しているものを削除すると1組だけ削除される System.out.println("Multimap#remove(key2, data1) -> " + multiMap.remove("key2", "data1")); System.out.println("--- After Multimap#toString: " + multiMap);
■実行結果
--- Before Multimap#toString: {key1=[value1, value2, value3], key2=[data1, data2, data3, data4, data1, data2], key3=[Taro]} Multimap#remove(key1, value3) -> true Multimap#remove(key2, value3) -> false Multimap#remove(key2, data1) -> true --- After Multimap#toString: {key1=[value1, value2], key2=[data2, data3, data4, data1, data2], key3=[Taro]}
特定のkeyに紐づく全エントリを削除する
Multimap#removeAll
を使うことで、指定のkeyに紐づく全エントリを削除することができます。
■サンプルコード
System.out.println("--- Before Multimap#toString: " + multiMap); // MultiMap#removeAll(key) で 指定のkeyの要素が全て削除される System.out.println("Multimap#removeAll(key2) -> " + multiMap.removeAll("key2")); System.out.println("--- After Multimap#toString: " + multiMap);
■実行結果
--- Before Multimap#toString: {key1=[value1, value2], key2=[data2, data3, data4, data1, data2], key3=[Taro]} Multimap#removeAll(key2) -> [data2, data3, data4, data1, data2] --- After Multimap#toString: {key1=[value1, value2], key3=[Taro]}
特定のkeyに紐づく値をまとめて置き換える
Multimap#replaceValues
を使うことで、指定のkeyに紐づくエントリを置き換えることができます
■サンプルコード
System.out.println("--- Before Multimap#toString: " + multiMap); List<String> valuesNew = Arrays.asList("Jiro", "Ken", "John"); multiMap.replaceValues("key3", valuesNew); System.out.println("--- After Multimap#toString: " + multiMap);
■実行結果
--- Before Multimap#toString: {key1=[value1, value2], key3=[Taro]} --- After Multimap#toString: {key1=[value1, value2], key3=[Jiro, Ken, John]}
java.util.Mapに変換する
Multimap#asMap
を使うことで、java.util.Mapで受け取ることができます
■サンプルコード
System.out.println("--- Src Multimap#toString: " + multiMap); Map<String, Collection<String>> map = multiMap.asMap(); System.out.println("--- Dest Map#toString: " + map);
■実行結果
--- Src Multimap#toString: {key1=[value1, value2], key3=[Jiro, Ken, John]} --- Dest Map#toString: {key1=[value1, value2], key3=[Jiro, Ken, John]}
試したソースコードの全体は以下の通りです
import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; public class MultimapClient { public static void main(String[] args) { // Java標準のやり方(対象keyに関連付けが無い場合はValueをnewして設定する処理を書く必要がある) Map<String, List<String>> normalMap = new HashMap<String, List<String>>(); if (!normalMap.containsKey("key1")) { normalMap.put("key1", new ArrayList<String>()); } normalMap.get("key1").add("value1"); if (!normalMap.containsKey("key1")) { normalMap.put("key1", new ArrayList<String>()); } normalMap.get("key1").add("value2"); if (!normalMap.containsKey("key1")) { normalMap.put("key1", new ArrayList<String>()); } normalMap.get("key1").add("value3"); if (!normalMap.containsKey("key2")) { normalMap.put("key2", new ArrayList<String>()); } normalMap.get("key2").add("data1"); if (!normalMap.containsKey("key3")) { normalMap.put("key3", new ArrayList<String>()); } normalMap.get("key3").add("Taro"); for (Map.Entry<String, List<String>> entry : normalMap.entrySet()) { System.out.println("HashMap::" + entry.getKey() + " -> " + entry.getValue()); } System.out.println("-------------------"); // MultiMapを使用したやり方 Multimap<String, String> multiMap = ArrayListMultimap.create(); multiMap.put("key1", "value1"); multiMap.put("key1", "value2"); multiMap.put("key1", "value3"); multiMap.put("key2", "data1"); multiMap.put("key3", "Taro"); System.out.println("Multimap#toString: " + multiMap); // MultiMap#entries を使えば 全要素に個別にアクセス可能 for (Entry<String, String> entry : multiMap.entries()) { System.out.println("MultiMap::" + entry.getKey() + " -> " + entry.getValue()); } // putAllで特定のkeyに複数のvalueをまとめて追加できる System.out.println("\n### Multimap#putAll"); System.out.println("--- Before Multimap#toString: " + multiMap); List<String> values = Arrays.asList("data2", "data3", "data4", "data1", "data2"); multiMap.putAll("key2", values); System.out.println("--- After Multimap#toString: " + multiMap); System.out.println("\n### Multimap#containsValue"); System.out.println("Multimap#toString: " + multiMap); // 特定のvalueが存在するかを個別にチェックできる System.out.println("Multimap#containsValue(data3) -> " + multiMap.containsValue("data3")); System.out.println("Multimap#containsValue(data999) -> " + multiMap.containsValue("data999")); System.out.println("\n### Multimap#containsEntry"); System.out.println("Multimap#toString: " + multiMap); // 特定のkeyとvalueの組み合わせが存在するかをチェックできる System.out.println("Multimap#containsEntry(key1, data3) -> " + multiMap.containsEntry("key1", "data3")); System.out.println("Multimap#containsEntry(key2, data3) -> " + multiMap.containsEntry("key2", "data3")); System.out.println("Multimap#containsEntry(key99, value1) -> " + multiMap.containsEntry("key99", "value1")); System.out.println("\n### Multimap#keys"); System.out.println("Multimap#toString: " + multiMap); // Multimap#keys で 全keyをMultisetで受け取れる(個別のvalue分のkeyが格納されている) Multiset<String> keys = multiMap.keys(); System.out.println("Multimap#keys -> " + keys); System.out.println("\n### Multimap#remove"); System.out.println("--- Before Multimap#toString: " + multiMap); // MultiMap#remove(key, value) で 指定のkeyとvalueの組み合わせの要素だけ削除する // 存在する組み合わせなので戻り値trueが返る System.out.println("Multimap#remove(key1, value3) -> " + multiMap.remove("key1", "value3")); // 存在しない組み合わせなので戻り値falseが返る System.out.println("Multimap#remove(key2, value3) -> " + multiMap.remove("key2", "value3")); // 対象の組み合わせが複数存在しているものを削除すると1組だけ削除される System.out.println("Multimap#remove(key2, data1) -> " + multiMap.remove("key2", "data1")); System.out.println("--- After Multimap#toString: " + multiMap); System.out.println("\n### Multimap#removeAll"); System.out.println("--- Before Multimap#toString: " + multiMap); // MultiMap#removeAll(key) で 指定のkeyの要素が全て削除される System.out.println("Multimap#removeAll(key2) -> " + multiMap.removeAll("key2")); System.out.println("--- After Multimap#toString: " + multiMap); // replaceValuesで特定のkeyに紐づく値をまとめて置き換える System.out.println("\n### Multimap#replaceValues"); System.out.println("--- Before Multimap#toString: " + multiMap); List<String> valuesNew = Arrays.asList("Jiro", "Ken", "John"); multiMap.replaceValues("key3", valuesNew); System.out.println("--- After Multimap#toString: " + multiMap); // asMapでjava.util.Mapで受け取ることができる System.out.println("\n### Multimap#map"); System.out.println("--- Src Multimap#toString: " + multiMap); Map<String, Collection<String>> map = multiMap.asMap(); System.out.println("--- Dest Map#toString: " + map); } }