您无法按多个键对单个项目进行分组,除非您接受该项目可能出现在多个组中。在这种情况下,您希望执行一种操作。flatMap
实现这一目标的一种方法是在收集之前与临时配对一起使用并保持组合。由于缺少标准对类型,因此典型的解决方案是用于: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.getKey1(), sub)))
.collect(Collectors.groupingBy(AbstractMap.SimpleImmutableEntry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.groupingBy(SubItem::getKey2))));
不需要这些临时对象的替代方法是直接在收集器中执行操作,但不幸的是,直到Java 9才会出现flatMapping
。flatMap
这样,解决方案看起来像这样
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]));
}