Java 8 Nested (Multi level) group by
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39130122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8 Nested (Multi level) group by
提问by Sandesh Kumar
I have few classes like below
我有几个像下面这样的课程
class Pojo {
List<Item> items;
}
class Item {
T key1;
List<SubItem> subItems;
}
class SubItem {
V key2;
Object otherAttribute1;
}
I want to aggregate the items based on key1
and for each aggregation, subitems should be aggregated by key2
in following way:
我想根据key1
每个聚合来聚合项目,子项目应该通过key2
以下方式聚合:
Map<T, Map<V, List<Subitem>>
How is this possible with Java 8 Collectors.groupingBy
nesting?
Java 8Collectors.groupingBy
嵌套怎么可能做到这一点?
I was trying something and stuck halfway at
我正在尝试一些东西并卡在了一半
pojo.getItems()
.stream()
.collect(
Collectors.groupingBy(Item::getKey1, /* How to group by here SubItem::getKey2*/)
);
Note: This not same as cascaded groupingBy
which does multilevel aggregation based on fields in the same object as discussed here
注:这不一样的级联groupingBy
,其不基于在同一个对象的字段多聚集的讨论在这里
回答by Holger
You can't group a single item by multiple keys, unless you accept the item to potentially appear in multiple groups. In that case, you want to perform a kind of flatMap
operation.
您不能通过多个键对单个项目进行分组,除非您接受该项目可能出现在多个组中。在这种情况下,您想要执行某种flatMap
操作。
One way to achieve this, is to use Stream.flatMap
with a temporary pair holding the combinations of Item
and SubItem
before collecting. Due to the absence of a standard pair type, a typical solution is to use Map.Entry
for that:
实现此目的的一种方法是使用Stream.flatMap
一个临时对来保存Item
和SubItem
收集之前的组合。由于没有标准对类型,典型的解决方案是使用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))));
An alternative not requiring these temporary objects would be performing the flatMap
operation right in the collector, but unfortunately, flatMapping
won't be there until Java?9.
另一种不需要这些临时对象的方法是flatMap
在收集器中执行操作,但不幸的flatMapping
是,直到 Java?9 才会出现。
With that, the solution would look like
有了这个,解决方案看起来像
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))));
and if we don't want to wait for Java?9 for that, we may add a similar collector to our code base, as it's not so hard to implement:
如果我们不想等待 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]));
}