You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 8 如何对List<Map>按name分组并聚合其他字段为列表?

按name分组并聚合其他字段为列表的实现方案

你的需求是把同name的集合元素中,除name外的其他字段值分别聚合成列表,而不是把整个Map对象放到列表里。原来的groupingBy只是完成了分组,但没有做字段级的聚合,我们可以通过分组后二次处理或者在分组时使用collectingAndThen进行转换来实现。

方案一:分步实现(直观易懂)

先按name完成分组,再对每个分组内的Map集合进行字段聚合:

import java.util.*;
import java.util.stream.Collectors;

public class GroupAggregateExample {
    public static void main(String[] args) {
        List<Map<String, Object>> allObjs = new ArrayList<>();
        Map<String, Object> src = new HashMap<>();
        src.put("name", "asset1");
        src.put("description", "desc1");
        src.put("definition", "def1");
        Map<String, Object> src2 = new HashMap<>();
        src2.put("name", "asset1");
        src2.put("description", "desc2");
        src2.put("definition", "def2");
        allObjs.add(src);
        allObjs.add(src2);

        // 1. 先按name分组,得到{name: [List<Map>]}的结构
        Map<String, List<Map<String, Object>>> groupedByName = allObjs.stream()
                .collect(Collectors.groupingBy(p -> (String) p.get("name")));

        // 2. 转换为期望的聚合结构
        Map<String, Map<String, Object>> finalResult = new HashMap<>();
        groupedByName.forEach((name, mapList) -> {
            Map<String, Object> aggregatedMap = new HashMap<>();
            aggregatedMap.put("name", name);

            // 收集所有非name的字段,将每个字段的值聚合成列表
            // 先获取所有非name的key(考虑到不同Map可能有不同key的情况)
            Set<String> nonNameKeys = mapList.stream()
                    .flatMap(map -> map.keySet().stream())
                    .filter(key -> !"name".equals(key))
                    .collect(Collectors.toSet());

            nonNameKeys.forEach(key -> {
                List<Object> fieldValues = mapList.stream()
                        .map(map -> map.get(key))
                        .filter(Objects::nonNull) // 过滤掉不存在该key的Map的null值
                        .collect(Collectors.toList());
                aggregatedMap.put(key, fieldValues);
            });

            finalResult.put(name, aggregatedMap);
        });

        // 输出结果,对于你的示例,这里会得到{asset1={name=asset1, description=[desc1, desc2], definition=[def1, def2]}}
        System.out.println(finalResult);
        // 如果只需要单个分组的结果(比如只有一个name),可以直接取:
        // Map<String, Object> singleResult = finalResult.values().iterator().next();
    }
}

方案二:流式API链式调用(更简洁)

利用Collectors.collectingAndThen在分组完成后直接对结果进行转换,一步到位:

Map<String, Map<String, Object>> finalResult = allObjs.stream()
        .collect(Collectors.groupingBy(
                p -> (String) p.get("name"),
                Collectors.collectingAndThen(
                        Collectors.toList(),
                        mapList -> {
                            Map<String, Object> aggregatedMap = new HashMap<>();
                            aggregatedMap.put("name", mapList.get(0).get("name"));

                            Set<String> nonNameKeys = mapList.stream()
                                    .flatMap(map -> map.keySet().stream())
                                    .filter(key -> !"name".equals(key))
                                    .collect(Collectors.toSet());

                            nonNameKeys.forEach(key -> {
                                List<Object> fieldValues = mapList.stream()
                                        .map(map -> map.get(key))
                                        .filter(Objects::nonNull)
                                        .collect(Collectors.toList());
                                aggregatedMap.put(key, fieldValues);
                            });

                            return aggregatedMap;
                        }
                )
        ));

关键说明

  • 原来的groupingBy(p -> p.get("name"))只是将同name的整个Map对象收集到列表中,而我们需要的是对每个字段单独聚合。
  • 上面的代码考虑了健壮性:如果分组内的Map存在不同的key(比如有的Map没有description字段),会自动收集所有出现过的非namekey,并过滤掉null值。
  • 如果你的业务场景中,所有Map的key都是完全一致的,可以简化代码,直接取第一个Map的key进行处理,不需要收集所有key。

内容的提问来源于stack exchange,提问作者Nishanth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 07:27:36