Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

Java | 複合キーのMap>取得ユーティリティ

概要

複合キーのMap>取得ユーティリティについてまとめます

詳細

Map>のKが複数キーを持つデータクラスとなるMapを
Listから取得するユーティリティを作成する。

KのすべてのカラムはTと同じ名前、同じデータ型であることを前提とする。

代表的な用途

テーブルから取得したデータリストを任意の複合キーごとに分割したリストに詰め替えたい場合に利用する。

コード

ListUtil

package gr.java_conf.tb.tbpg_util.lang;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * List関連ユーティリティ。
 */
public class ListUtil {
  /** ユーティリティのためコンストラクタを隠蔽 */
  private ListUtil() {
  };

  /**
   * リスト{@code datas}のデータを{@code key}をキーとしたMapに詰め替える。
   *
   * <pre>
   * 前提1:リストの型{@code T}とMapのキーとなる{@code K}は同一名称のフィールドを持つ。
   * 前提2:{@code K}は全てのフィールドをキーとするequals、hashCodeを実装していること。
   * </pre>
   *
   * @param datas 対象リスト
   * @param key Mapに指定しるKeyとなるデータクラス
   * @return リスト{@code datas}のデータを{@code key}をキーとしたMap
   * @throws SecurityException セキュリティーマネージャー s が存在し、次の条件のどれかが満たされる場合. <br />
   *           ・s.checkMemberAccess(this,
   *           Member.PUBLIC)の呼び出しがこのフィールドへのアクセスを許可しない<br />
   *           ・呼び出し側のクラスローダーが同じでないか、現在のクラスローダーの上位クラスローダーと
   *           s.checkPackageAccess()<br />
   *           の呼び出しがこのクラスのパッケージへのアクセスを許可しない
   * @throws NoSuchFieldException メソッドが見つからなかった場合
   * @throws IllegalAccessException 基本となるフィールドにアクセスできない場合
   * @throws IllegalArgumentException 指定されたオブジェクトが基本となるフィールド (またはそのサブクラスか実装側)
   *           を宣言するクラスまたはインタフェースのインスタンスではない場合
   * @throws InstantiationException この Class が abstract
   *           クラス、インタフェース、配列クラス、プリミティブ型、または void
   *           を表す場合、クラスが引数なしのコンストラクタを保持しない場合、あるいはインスタンスの生成がほかの理由で失敗した場合
   */
  public static <K, T> Map<K, List<T>> getMultiKeyMapList(List<T> datas, Class<K> keyClass) throws SecurityException,
      IllegalArgumentException, NoSuchFieldException, IllegalAccessException, InstantiationException {
    Map<K, List<T>> map = new HashMap<K, List<T>>();
    for (T data : datas) {
      K key = keyClass.newInstance();
      for (Field field : keyClass.getDeclaredFields()) {
        // ※ReflectionUtil部分は自作のユーティリティ。リフレクションでアクセサの操作を行なっています
        ReflectionUtil.setField(key, field.getName(), ReflectionUtil.getField(data, field.getName()));
      }
      if (!map.containsKey(key)) {
        map.put(key, new ArrayList<T>());
      }
      List<T> list = map.get(key);
      list.add(data);
    }
    return map;

  }
}

ListUtilTest

package gr.java_conf.tb.tbpg_util.lang;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.junit.experimental.runners.Enclosed;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Enclosed.class)
public class ListUtilTest {
  @RunWith(Theories.class)
  public static class GetMultiKeyMapList {
    @SuppressWarnings("unused")
    private static class Hoge {
      private String hogeStr;
      private Integer hogeInt;
      private Boolean hogeBool;

      public Hoge() {

      }

      public Hoge(String hogeStr, Integer hogeInt, Boolean hogeBool) {
        super();
        this.hogeStr = hogeStr;
        this.hogeInt = hogeInt;
        this.hogeBool = hogeBool;
      }

      public String getHogeStr() {
        return hogeStr;
      }

      public void setHogeStr(String hogeStr) {
        this.hogeStr = hogeStr;
      }

      public Integer getHogeInt() {
        return hogeInt;
      }

      public void setHogeInt(Integer hogeInt) {
        this.hogeInt = hogeInt;
      }

      public Boolean getHogeBool() {
        return hogeBool;
      }

      public void setHogeBool(Boolean hogeBool) {
        this.hogeBool = hogeBool;
      }

      @Override
      public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((hogeBool == null) ? 0 : hogeBool.hashCode());
        result = prime * result + ((hogeInt == null) ? 0 : hogeInt.hashCode());
        result = prime * result + ((hogeStr == null) ? 0 : hogeStr.hashCode());
        return result;
      }

      @Override
      public boolean equals(Object obj) {
        if (this == obj)
          return true;
        if (obj == null)
          return false;
        if (getClass() != obj.getClass())
          return false;
        Hoge other = (Hoge) obj;
        if (hogeBool == null) {
          if (other.hogeBool != null)
            return false;
        } else if (!hogeBool.equals(other.hogeBool))
          return false;
        if (hogeInt == null) {
          if (other.hogeInt != null)
            return false;
        } else if (!hogeInt.equals(other.hogeInt))
          return false;
        if (hogeStr == null) {
          if (other.hogeStr != null)
            return false;
        } else if (!hogeStr.equals(other.hogeStr))
          return false;
        return true;
      }

      @Override
      public String toString() {
        return "Hoge [hogeStr=" + hogeStr + ", hogeInt=" + hogeInt + ", hogeBool=" + hogeBool + "]";
      }
    }

    @SuppressWarnings("unused")
    private static class Hige {
      private String hogeStr;
      private Integer hogeInt;
      private Boolean hogeBool;
      private String higeStr;
      private Integer higeInt;
      private Boolean higeBool;

      public Hige() {

      }

      public Hige(String hogeStr, Integer hogeInt, Boolean hogeBool, String higeStr, Integer higeInt, Boolean higeBool) {
        super();
        this.hogeStr = hogeStr;
        this.hogeInt = hogeInt;
        this.hogeBool = hogeBool;
        this.higeStr = higeStr;
        this.higeInt = higeInt;
        this.higeBool = higeBool;
      }

      public String getHogeStr() {
        return hogeStr;
      }

      public void setHogeStr(String hogeStr) {
        this.hogeStr = hogeStr;
      }

      public Integer getHogeInt() {
        return hogeInt;
      }

      public void setHogeInt(Integer hogeInt) {
        this.hogeInt = hogeInt;
      }

      public Boolean getHogeBool() {
        return hogeBool;
      }

      public void setHogeBool(Boolean hogeBool) {
        this.hogeBool = hogeBool;
      }

      public String getHigeStr() {
        return higeStr;
      }

      public void setHigeStr(String higeStr) {
        this.higeStr = higeStr;
      }

      public Integer getHigeInt() {
        return higeInt;
      }

      public void setHigeInt(Integer higeInt) {
        this.higeInt = higeInt;
      }

      public Boolean getHigeBool() {
        return higeBool;
      }

      public void setHigeBool(Boolean higeBool) {
        this.higeBool = higeBool;
      }
    }

    @Theory
    public void testGetMultiKeyMapList() throws SecurityException, NoSuchFieldException, IllegalArgumentException,
        IllegalAccessException, InstantiationException {
      List<Hige> datas = new ArrayList<Hige>();
      Hige hige1 = new Hige("hoge", Integer.valueOf(10), Boolean.valueOf(true), "hige1", Integer.valueOf(10),
          Boolean.valueOf(true));
      datas.add(hige1);
      Hige hige2 = new Hige("hoge", Integer.valueOf(10), Boolean.valueOf(true), "hige2", Integer.valueOf(20),
          Boolean.valueOf(false));
      datas.add(hige2);
      Hige hige3 = new Hige("hoge", Integer.valueOf(30), Boolean.valueOf(true), "hige3", Integer.valueOf(30),
          Boolean.valueOf(true));
      datas.add(hige3);

      Hoge hoge1 = new Hoge("hoge", Integer.valueOf(10), Boolean.valueOf(true));
      Map<Hoge, List<Hige>> multiKeyMap = ListUtil.getMultiKeyMapList(datas, Hoge.class);
      List<Hige> actual1 = multiKeyMap.get(hoge1);
      assertThat(actual1.size(), is(Integer.valueOf(2)));

      for (Entry<Hoge, List<Hige>> entry : multiKeyMap.entrySet()) {
        System.out.print(entry.getKey());
        System.out.println(entry.getValue());
      }
    }
  }

}

出力内容

Hoge [hogeStr=hoge, hogeInt=10, hogeBool=true][Hige [hogeStr=hoge, hogeInt=10, hogeBool=true, higeStr=hige1, higeInt=10, higeBool=true], Hige [hogeStr=hoge, hogeInt=10, hogeBool=true, higeStr=hige2, higeInt=20, higeBool=false]]
Hoge [hogeStr=hoge, hogeInt=30, hogeBool=true][Hige [hogeStr=hoge, hogeInt=30, hogeBool=true, higeStr=hige3, higeInt=30, higeBool=true]]