如何用flatMap流式声明式写法替代foreach生成指定结构HashMap
用Java流式API实现嵌套集合的分组与KPI合并去重
核心实现(Java 9+)
利用flatMap层层展开嵌套集合,结合Collectors.groupingBy和Collectors.flatMapping实现需求,代码更简洁且符合声明式风格:
import java.util.AbstractMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; private Map<String, List<String>> getViewKpis(List<Role> roles) { return roles.stream() // 展开所有角色包含的视图 .flatMap(role -> role.getViews().stream()) // 将每个视图与子视图配对,生成「键-KPI列表」的条目 .flatMap(view -> view.getSubViews().stream() .map(subView -> new AbstractMap.SimpleEntry<>( String.join("|", view.getViewName(), subView.getSubViewName()), subView.getKpis()))) // 按键分组,合并所有KPI并去重 .collect(Collectors.groupingBy( AbstractMap.SimpleEntry::getKey, Collectors.flatMapping( entry -> entry.getValue().stream(), Collectors.collectingAndThen( Collectors.toList(), kpis -> kpis.stream().distinct().collect(Collectors.toList()) ) ) )); }
可读性优化版本(Java 16+)
使用Java 16引入的record定义简单数据容器,替代AbstractMap.SimpleEntry,代码可读性更强:
import java.util.List; import java.util.Map; import java.util.stream.Collectors; // 定义键与KPI列表的配对记录 private record KeyKpiPair(String key, List<String> kpis) {} private Map<String, List<String>> getViewKpis(List<Role> roles) { return roles.stream() .flatMap(role -> role.getViews().stream()) .flatMap(view -> view.getSubViews().stream() .map(subView -> new KeyKpiPair( String.join("|", view.getViewName(), subView.getSubViewName()), subView.getKpis()))) .collect(Collectors.groupingBy( KeyKpiPair::key, Collectors.flatMapping( pair -> pair.kpis().stream(), Collectors.collectingAndThen( Collectors.toList(), kpis -> kpis.stream().distinct().collect(Collectors.toList()) ) ) )); }
Java 8兼容版本
由于Java 8没有Collectors.flatMapping,需要分两步处理:先分组得到嵌套列表,再展开去重:
import java.util.AbstractMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; private Map<String, List<String>> getViewKpis(List<Role> roles) { // 第一步:按键分组,得到键对应多个KPI列表的嵌套结构 Map<String, List<List<String>>> grouped = roles.stream() .flatMap(role -> role.getViews().stream()) .flatMap(view -> view.getSubViews().stream() .map(subView -> new AbstractMap.SimpleEntry<>( String.join("|", view.getViewName(), subView.getSubViewName()), subView.getKpis()))) .collect(Collectors.groupingBy( AbstractMap.SimpleEntry::getKey, Collectors.mapping(AbstractMap.SimpleEntry::getValue, Collectors.toList()) )); // 第二步:展开嵌套列表,去重后生成最终Map return grouped.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> entry.getValue().stream() .flatMap(List::stream) .distinct() .collect(Collectors.toList()) )); }
代码逻辑说明
- 展开嵌套集合:通过两次
flatMap,依次将Role→View→SubView的嵌套结构展开为扁平的条目流; - 生成分组键:为每个
SubView生成viewName|subViewName格式的键,并关联对应的KPI列表; - 分组与合并去重:通过分组收集所有对应键的KPI,再展开并去重,得到与原foreach实现完全一致的结果。
内容的提问来源于stack exchange,提问作者Moussa ELaQ
相关产品推荐
相关产品推荐

