对groupingBy的结果做缩减(flatMap a groupingBy result)
收集器与多级缩减示例。
IntBox.java
public class IntBox {
private String name;
private List<Integer> nums = new ArrayList<>();
// set get constructor 略
}
对于类似IntBox(有属性为集合)的对象的集合List<IntBox>
,常有需要获得Map<String, List<Integer>>
这样的数据结构。
考虑到name可能重复,所以不可以使用toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
,使用groupingBy也无法获得所需数据结构。
Map<String, List<Integer>> collect = dataList.stream().collect(toMap(IntBox::getName, IntBox::getNums));
Map<String, List<IntBox>> collect = dataList.stream().collect(groupingBy(IntBox::getName));
Map<String, List<List<Integer>>> collect = dataList.stream().collect(groupingBy(IntBox::getName, mapping(IntBox::getNums, toList())));
此时可以通过提供mergeFunction,来使用toMap完成转换。toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction)
Map<String, List<Integer>> collect2 = dataList.stream().collect(toMap(IntBox::getName, IntBox::getNums,
(v1, v2) -> {
v1.addAll(v2);
return v1;
}));
但是mergeFunction只能返回List,并且mergeFunction会改变原有IntBox对象中集合属性nums中的数据。
可以优化为进行groupingBy操作,并且采用downstream做转换,这样会更加灵活一点。
核心在于Collector.of
,如果需要对结果进行去重,可以提供HashSet,结果就是Map<String, Set<Integer>>
。groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
Map<String, List<Integer>> collect = dataList.stream().collect(groupingBy(IntBox::getName, mapping(IntBox::getNums,
Collector.of(ArrayList::new, List::addAll, (v1, v2) -> {
v1.addAll(v2);
return v1;
}))));
Map<String, Set<Integer>> collect = dataList.stream().collect(groupingBy(IntBox::getName, mapping(IntBox::getNums,
Collector.of(HashSet::new, Set::addAll, (v1, v2) -> {
v1.addAll(v2);
return v1;
}))));
上面都是针对于JDK 8的情况,在JDK 9中可以通过flatMapping,一个新的收集器,来实现同样的需求。
Map<String, List<Integer>> collect = dataList.stream()
.collect(groupingBy(IntBox::getName, flatMapping(x -> x.getNums().stream(), toList())));