デトフィア

プログラミング

Mapのunionを行い、keyが重複していたら例外とする

org.apache.commons.collections4.CollectionUtilsは便利な上にソースコードも優しい。 例えばaddIgnoreNullメソッドは安全にnull以外の要素をコレクションに追加してくれる

public static <T> boolean addIgnoreNull(final Collection<T> collection, final T object) {
    if (collection == null) {
        throw new NullPointerException("The collection must not be null");
    }
    return object != null && collection.add(object);
}

以下のように使います

@Test
public void test_collection_utils_apache(){
    List<String> names = new ArrayList<>();
    names.add("nameA");
    names.add("nameB");
    CollectionUtils.addIgnoreNull(names,"nameC");
    CollectionUtils.addIgnoreNull(names,null);
    assertEquals(3,names.size());
}

nullを渡してもそれは追加されずにスルーされます。 当然ですがジェネリクスを利用しているので型が異なる要素も入れることはできません

次にuinonでコレクション同士の結合を行ないます

@Test
public void test_collection_utils_apache(){
    List<String> names = new ArrayList<>();
    names.add("nameA");
    names.add("nameB");

    List<String> tags = new ArrayList<>();
    tags.add("tagA");
    tags.add("tagB");

    Collection<String> newList = CollectionUtils.union(names,tags);
    
    newList.stream().forEach(System.out::println);
    assertEquals(4,newList.size());
}

このように便利なメソッドがいくつも用意されています

今回はこのようなコレクションに対する機能としてMapのunionを作成します。 MapにはputAllというメソッドがありますが、これは同一キーがあった場合は上書きされるのとvoidメソッドであるという特徴があります

Map<String,String> mapA = new HashMap<>();
mapA.put("keyA","valueA");
mapA.put("keyB","valueB");
mapA.put("keyC","valueC");

Map<String,String> mapB = Map.of(
        "keyA","valueC",
        "keyD","valueD",
        "keyE","valueE"
);
mapA.putAll(mapB);
  • mapAをofで作っていないのは、ofで作ると要素数が固定になるからです

今回作成するunion機能はこんな感じでシンプルです

  • 戻り値は新しいMapオブジェクト
  • 同一keyのものがあったら例外を吐く
public static <T,R> Map<T,R> union(Map<T,R> mapa, Map<T,R> mapb){
    for(T key : mapa.keySet()){
        if(mapb.containsKey(key)){
            throw new RuntimeException("重複したkeyが存在します");
        }
    }
    Map<T,R> result = new HashMap<>();
    result.putAll(mapa);
    result.putAll(mapb);
    return result;
}

テストしてみましょう

@Test
public void test_my_map_ng(){
    Map<String,String> mapA = new HashMap<>();
    mapA.put("keyA","valueA");
    mapA.put("keyB","valueB");
    mapA.put("keyC","valueC");
    Map<String,String> mapB = Map.of(
            "keyA","valueC",
            "keyD","valueD",
            "keyE","valueE"
    );
    assertThrows(RuntimeException.class, () -> MyMapUtils.union(mapA,mapB));
}

@Test
public void test_my_map_ok(){
    Map<String,String> mapA = Map.of(
            "keyA","valueC",
            "keyB","valueD",
            "keyC","valueE"
    );
    Map<String,String> mapB = Map.of(
            "keyD","valueC",
            "keyE","valueD",
            "keyF","valueE"
    );
    Map<String,String> newMap = MyMapUtils.union(mapA,mapB);
    assertEquals(6,newMap.size());
}