リストからグルーピングで多重Mapを作ろう。 (Java)

Collectors::groupingBy

JavaのListから、グループ化するためによく使うCollectors::groupingBy ですが、
グルーピングされた要素は、特定のキーごとにリスト化されます。

List<V> から、特定のキーで
Map<K, List<V>> こんな構造にする事ができます。

Map<K, List<V>> サンプル

サンプルで、こんなa,bのメンバ変数をもつ単純なクラスがあるとして、
これのリストから、aの値でグルーピングしたMapを作成してみましょう。

class Sample {
  int a;
  int b;

  public Sample(int a, int b) {
    this.a = a;
    this.b = b;
  }

  @Override
  public String toString() {
    return "Sample{" +
            "a=" + a +
            ", b=" + b +
            '}';
  }
}

適当にリストを用意

List<Sample> list = Arrays.asList(
  new Sample(1, 11),
  new Sample(1, 12),
  new Sample(2, 21),
  new Sample(2, 22),
  new Sample(2, 23),
  new Sample(2, 24),
  new Sample(3, 31),
  new Sample(3, 32),
  new Sample(3, 33));

グルーピング化してみます。

// aでグルーピングする
Map<Integer, List<Sample>> map1 = list.stream()
        .collect(Collectors.groupingBy(o -> o.a));

// テキスト出力
for (Map.Entry<Integer, List<Sample>> entry : map1.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

Collectors.groupingBy(o -> o.a) で、グルーピングします。
出力結果は以下のように、aの値によって、グルーピングされた要素がリストになります。

Map<Integer, List<Sample>>

1: [Sample{a=1, b=11}, Sample{a=1, b=12}]
2: [Sample{a=2, b=21}, Sample{a=2, b=22}, Sample{a=2, b=23}, Sample{a=2, b=24}]
3: [Sample{a=3, b=31}, Sample{a=3, b=32}, Sample{a=3, b=33}]

多重Mapにしたい

このList<V> の部分を、さらにMap化したいときは、
Collectors::groupingBy の第二引数にさらに、reductionを指定する事で実現できます。

Map<K1, Map<K2, V>> こんな構造にしたいと思います。
K1がメンバ変数a、K2がメンバ変数bになるようにします。

Map<K1, Map<K2, V>> サンプル

// aでグルーピングして、更にbでMap化
Map<Integer, Map<Integer, Sample>> map2 = list.stream()
        .collect(Collectors.groupingBy(o -> o.a, Collectors.toMap(o -> o.b, o -> o)));

// テキスト出力
for (Map.Entry<Integer, Map<Integer, Sample>> entry : map2.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

Collectors.groupingBy(o -> o.a, Collectors.toMap(o -> o.b, o -> o))
groupingByの第二引数に、更に下流となるreductionを指定しています。

1: {11=Sample{a=1, b=11}, 12=Sample{a=1, b=12}}
2: {21=Sample{a=2, b=21}, 22=Sample{a=2, b=22}, 23=Sample{a=2, b=23}, 24=Sample{a=2, b=24}}
3: {32=Sample{a=3, b=32}, 33=Sample{a=3, b=33}, 31=Sample{a=3, b=31}}

出力結果は上記のように、
Map<Integer, Map<Integer, Sample>>という形にすることができました。

ちなみに、a,b共に同一の要素が存在すると、下記のように例外が発生します。
まぁキーがバッティングした場合、よしなに上書きされるよりは例外が出てくれる方が良いでしょう。

Exception in thread "main" java.lang.IllegalStateException: Duplicate key Sample{a=2, b=24}

おしまい。

groupingByの第二引数は、downstream reduction となっていて、
Collectors.toSet()や、さらにgroupingByを指定することもできます。
for文でくるくる回して書いていたものが、結構スッキリ書けるようになるので、使ってみてはいかがでしょう。

コメントを残す