リストなどコレクションを Map にする構造では、キー存在有無によってどうしても
初期リスト、コレクションを格納しなければならない。
Map<String, List<String>> map = new HashMap<>();
if (!map.containsKey("a")) { map.put("a", new ArrayList<String>()); } map.get("a").add("100");
この if 文を毎回記述するのは避けたいとよく思っていた。
リストMap の リストに要素を追加(4) - Oboe吹きプログラマの黙示録
を書いてから、yipuran-core に入れた MapListpush は、
MapListpush<String, String> mlistpush = MapListpush.of(map); mlistpush.accept("a", "100");
で、if文を書かなくて済むようにするものだった、だがこれは、、
ofメソッド引数の型が、Map<String, List<String>> に限定するMapListpushの
総称型が、<String, String> なので、一見わかりにくい。
型が、List に限定される点は良いのだけれど、MapListpush として生成するラムダ式の中身は
決してパフォーマンスが良いものではない。
そこで、単純な思考ではあるが、この if 文の処理をラムダ式にして、リストに制約しない
ものにすれば良いのではということで、MapFunction という名称で
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/util/collection/MapFunction.java
を作成した。
すると、
MapFunction<String, List<String>> m = MapFunction.of(map, ArrayList::new);
と宣言することで、
m.apply("a").add("100");
で記述することができる。apply() の戻り値が、MapFunction の 型=V なので、
リストであれば、add , remove, addAll MapListpush が追加しかできないものだったのが
add以外の用途も広がる。
しかも、リストに限定しなくても良いはずで、配列のマップでも対応できる。
Map<String, Integer[]> mapAry = new HashMap<>();
キー"A" に、配列、2個目に値 112 を格納する場合、(配列の長さは決まっているのが条件)
MapFunction<String, Integer[]> mp = MapFunction.of(mapAry, ()->new Integer[3]);
と宣言すれば、以下で目的を達する。
mp.apply("A")[1] = 112;
配列のマップということでは、1年前に書いた MapArraypush も、悪くはないが、
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/util/collection/MapArraypush.java
これも、MapFunction で、代用できるし複雑ではないので良いのであろう。
package org.yipuran.util.collection; import java.util.Map; import java.util.function.Supplier; /** * Map 未登録キーの場合にSupplierで提供する値を約束して値を取得する. * <PRE> * MapFunction<String, List<String>> f = MapFunction.of(map, ArrayList::new); * f.apply("A").add("1"); * </PRE> * @param <K> * @param <V> */ @FunctionalInterface public interface MapFunction<K, V>{ V apply(K k); /** * Supplier 指定で、MapFunction インスタンスを生成 * @param map 対象Map * @param s VのSupplier * @return MapFunction */ static <K, V> MapFunction<K, V> of(Map<K, V> map, Supplier<V> s){ return k->{ if (!map.containsKey(k)) { map.put(k, s.get()); } return map.get(k); }; } }