Map の値にリストなどを持たせる場合の書きやすさと効率

リストなどコレクションを 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);
      };
   }
}