マップの比較、MapDiff

1年近く前に、リストの比較を書いた。
リストの比較、ListDiff - Oboe吹きプログラマの黙示録

それなら2つのマップの比較も普遍的なものが書けるであろう。

import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Consumer;

/**
 * MapDiff
 */
public final class MapDiff<K, V>{
   private BiFunction<V, V, Boolean> matchFunction;
   private Consumer<K> leftonly;
   private Consumer<K> rightonly;
   private Consumer<K> diffConsumer;
   private Consumer<K> matchConsumer;

   private MapDiff(BiFunction<V, V, Boolean> matchFunction){
      this.matchFunction = matchFunction;
   }
   /**
    * MapDiffインスタンス生成.
    * @param matchFunction 比較する2つのMapのValue要素を等しいと判定する BiFunction で Boolean(true=等しい) を返す。
    * @return ListDiff<T>{
    */
   public static <K, V> MapDiff<K, V> of(BiFunction<V, V, Boolean> matchFunction){
       return new MapDiff<>(matchFunction);
   }
   /**
    * 左側(1番目指定)Mapだけに所属するkeyの Consumerを登録.
    * @param leftonly Consumer<K>
    */
   public void leftOnly(Consumer<K> leftonly){
      this.leftonly = leftonly;
   }
   /**
    * 右側(2番目指定)Mapだけに所属するkeyの Consumerを登録.
    * @param rightonly Consumer<K>
    */
   public void rightOnly(Consumer<K> rightonly){
      this.rightonly = rightonly;
   }
   /**
    * MapのValue値が異なるkeyの Consumerを登録.
    * @param biConsumer Consumer<K>
    */
   public void unmatch(Consumer<K> diffConsumer){
      this.diffConsumer = diffConsumer;
   }
   /**
    * MapのKeyとValue値が同じkeyの Consumerを登録.
    * @param biConsumer Consumer<K>
    */
   public void match(Consumer<K> matchConsumer){
      this.matchConsumer = matchConsumer;
   }
   /**
    * Mapの比較.
    * @param leftMap
    * @param rightMap
    */
   public void diff(Map<K, V> leftMap, Map<K, V> rightMap){
      leftMap.entrySet().stream().forEach(e->{
         K k = e.getKey();
         if (rightMap.containsKey(k)){
            if (matchFunction.apply(e.getValue(), rightMap.get(k))){
               if (matchConsumer != null) matchConsumer.accept(k);
            }else{
               if (diffConsumer != null) diffConsumer.accept(k);
            }
         }else{
            if (leftonly != null) leftonly.accept(k);
         }
      });
      if (rightonly != null){
         rightMap.keySet().forEach(key->{
            if (!leftMap.containsKey(key)) rightonly.accept(key);
         });
      }
   }
   /**
    * Mapの比較.
    * @param map1 マップ1
    * @param map2 マップ2
    * @param match 値が一致するかを返す BiFunction<V, V, Boolean>
    * @param leftOnly マップ1に対してマップ2で削除されたキーのConsumer<K>
    * @param rightOnly マップ1に対してマップ2で追加されたキーのConsumer<K>
    * @param mc マップ1に対してマップ2で変更されたキーのConsumer<K>
    */
   public static <K, V> void diffMap(Map<K, V> map1, Map<K, V> map2, BiFunction<V, V, Boolean> match
   , Consumer<K> leftOnly, Consumer<K> rightOnly, Consumer<K> mc){
      map1.entrySet().stream().forEach(e->{
         K k = e.getKey();
         if (map2.containsKey(k)){
            if (!match.apply(e.getValue(), map2.get(k))) mc.accept(k);;
         }else{
            leftOnly.accept(k);
         }
      });
      map2.keySet().forEach(key->{
         if (!map1.containsKey(key)) rightOnly.accept(key);
      });
   }
}

だいたいこんなかんじで使う

MapDiff<Integer, String> m = MapDiff.of((v1, v2)->v1.equals(v2));
m.leftOnly(k->leftMap.put(k, map1.get(k)));
m.rightOnly(k->rightMap.put(k, map2.get(k)));
m.unmatch(k->dMap.put(k, map1.get(k) + "→" + map2.get(k)));

m.diff(map1, map2);