Java 8 ネストされた(マルチレベル)グループ by 質問する

Java 8 ネストされた(マルチレベル)グループ by 質問する

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

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注: これは、同じオブジェクト内のフィールドに基づいて複数レベルの集計を行うカスケードとは異なります。ここ

ベストアンサー1

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

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

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

これらの一時オブジェクトを必要としない代替案としては、flatMapコレクター内で直接操作を実行することですが、残念ながら、flatMappingJava 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]));
}

おすすめ記事