web-dev-qa-db-ja.com

Java 8ネスト(マルチレベル)グループ化

以下のようないくつかのクラスがあります

class Pojo {
    List<Item> items;
}

class Item {
    T key1;
    List<SubItem> subItems;
}

class SubItem {
    V key2;
    Object otherAttribute1;
}

key1に基づいてアイテムを集計したいのですが、集計ごとに、次の方法でkey2によってサブアイテムを集計する必要があります。

Map<T, Map<V, List<Subitem>>

Java 8 Collectors.groupingBy入れ子でこれをどのように行うことができますか?

私は何かを試していて、途中で立ち往生しました

pojo.getItems()
    .stream()
    .collect(
        Collectors.groupingBy(Item::getKey1, /* How to group by here SubItem::getKey2*/)
    );

注:これは、カスケードされたgroupingByとは異なります。これは、説明したように、同じオブジェクト内のフィールドに基づいてマルチレベルの集約を行います here

16
Sandesh Kumar

1つのアイテムを複数のキーでグループ化することはできません。ただし、アイテムが複数のグループに表示される可能性がある場合を除きます。その場合、一種のflatMap操作を実行する必要があります。

これを達成する1つの方法は、収集する前にItemSubItemの組み合わせを保持する一時的なペアでStream.flatMapを使用することです。標準のペアタイプがないため、一般的な解決策はMap.Entryを使用することです。

Map<T, Map<V, List<SubItem>>> result = pojo.getItems().stream()
    .flatMap(item -> item.subItems.stream()
        .map(sub -> new AbstractMap.SimpleImmutableEntry<>(item, sub)))
    .collect(Collectors.groupingBy(e -> e.getKey().getKey1(),
                Collectors.mapping(Map.Entry::getValue,
                    Collectors.groupingBy(SubItem::getKey2))));

これらの一時オブジェクトを必要としない別の方法は、コレクターでflatMap操作を実行することですが、残念ながら flatMapping はJava 9まで存在しません。

これで、ソリューションは次のようになります

Map<T, Map<V, List<SubItem>>> result = pojo.getItems().stream()
    .collect(Collectors.groupingBy(Item::getKey1,
                Collectors.flatMapping(item -> item.getSubItems().stream(),
                    Collectors.groupingBy(SubItem::getKey2))));

また、Java 9を待つ必要がない場合は、実装がそれほど難しくないため、コードベースに同様のコレクターを追加できます。

static <T,U,A,R> Collector<T,?,R> flatMapping(
    Function<? super T,? extends Stream<? extends U>> mapper,
    Collector<? super U,A,R> downstream) {

    BiConsumer<A, ? super U> acc = downstream.accumulator();
    return Collector.of(downstream.supplier(),
        (a, t) -> { try(Stream<? extends U> s=mapper.apply(t)) {
            if(s!=null) s.forEachOrdered(u -> acc.accept(a, u));
        }},
        downstream.combiner(), downstream.finisher(),
        downstream.characteristics().toArray(new Collector.Characteristics[0]));
}
22
Holger