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

如何用Java Stream API将扁平对象列表转为层级结构并去重Section

问题描述

我有一个扁平的对象列表,类定义如下:

public class FlatQuestion{
   public int QuestionId;
   public String QuestionName;
   public int SectionId;
   public String SectionName;
   public int FieldId;
   public String FieldName;
   public int Input_Type_Id;
   public String Input_Type_String;
}

需要将其转换为List<Question>的层级结构,目标类定义如下:

public class Question {
    public int id;
    public String name;
    public List<Section> sections;

    Question(String name, int id, List<Section> sections) {
        this.name = name;
        this.id = id;
        this.sections = sections;
    }
}

public class Section {
    public int id;
    public String name;
    public List<Field> fields;

    Section(int id, String name, List<Field> fields) {
        this.name = name;
        this.id = id;
        this.fields = fields;
    }
}

public class Field {
    public int id;
    public String label; // 修正原代码拼写错误:lable -> label
    public Input input_type;

    Field(int id, String label, Input input_type) {
        this.id = id;
        this.label = label;
        this.input_type = input_type;
    }
}

public class Input {
    public int id;
    public String type;

    Input(int id, String type) {
        this.id = id;
        this.type = type;
    }
}

我当前的实现会生成重复的Section,代码如下:

final List<Question> results = data
    .stream()
    // 提取去重的QuestionId
    .map(FlatQuestion::getQuestionId)
    .distinct()
    // 根据QuestionId构建Question
    .map(it -> 
         new Question(
             // 原代码参数顺序错误,Question构造器为name在前、id在后
             data.stream().filter(o -> o.getQuestionId() == it).findFirst().get().getQuestionName(),
             it, 
             // 提取当前Question下的Section,但会重复创建实例
             data
                 .stream()
                 .filter(o -> o.getQuestionId() == it)
                 .map(ing -> new Section(ing.getSectionId(), ing.getSectionName(), new ArrayList<>()))
                 .collect(Collectors.toList())
         )
    ).collect(Collectors.toList());

附带示例数据:

FlatQuestion f1 = new FlatQuestion(1, "Student Survey", 1, "Basic info", 1, "Enter Name", 1, "Text");
FlatQuestion f2 = new FlatQuestion(1, "Student Survey", 1, "Basic info", 2, "Enter Address", 1, "Text");
FlatQuestion f3 = new FlatQuestion(1, "Student Survey", 2, "Class Info", 3, "Select No of subjects", 2, "Dropdown");
FlatQuestion f4 = new FlatQuestion(1, "Student Survey", 2, "Class info", 4, "Enter primary subject", 1, "Text");
FlatQuestion f5 = new FlatQuestion(2, "Patient Audit", 3, "Basic info", 5, "Enter Name", 1, "Text");
FlatQuestion f6 = new FlatQuestion(2, "Patient Audit", 3, "Basic info", 6, "Enter Address", 1, "Text");
FlatQuestion f7 = new FlatQuestion(2, "Patient Audit", 4, "Alcohol Consumption", 7, "How often you drink", 3, "Checkbox");

请问如何修改代码以获取去重后的Section,完成扁平列表到层级结构的转换?


解决方案

核心思路是按层级分组后再构建对象,通过Collectors.groupingBy实现多级分组,确保每个Section只被创建一次,避免重复。

完整实现代码

// 1. 先按QuestionId分组,拆分每个Question对应的所有扁平数据
Map<Integer, List<FlatQuestion>> questionGroup = data.stream()
    .collect(Collectors.groupingBy(FlatQuestion::getQuestionId));

// 2. 遍历每个Question分组,构建最终层级结构
List<Question> results = questionGroup.entrySet().stream()
    .map(questionEntry -> {
        int questionId = questionEntry.getKey();
        List<FlatQuestion> questionFlats = questionEntry.getValue();
        // 获取Question名称(同一QuestionId下名称一致,取第一个即可)
        String questionName = questionFlats.get(0).getQuestionName();

        // 3. 在当前Question下,按SectionId分组
        Map<Integer, List<FlatQuestion>> sectionGroup = questionFlats.stream()
            .collect(Collectors.groupingBy(FlatQuestion::getSectionId));

        // 4. 遍历每个Section分组,构建Section对象
        List<Section> sections = sectionGroup.entrySet().stream()
            .map(sectionEntry -> {
                int sectionId = sectionEntry.getKey();
                List<FlatQuestion> sectionFlats = sectionEntry.getValue();
                String sectionName = sectionFlats.get(0).getSectionName();

                // 5. 构建当前Section下的所有Field和Input
                List<Field> fields = sectionFlats.stream()
                    .map(flat -> {
                        Input input = new Input(flat.getInput_Type_Id(), flat.getInput_Type_String());
                        return new Field(flat.getFieldId(), flat.getFieldName(), input);
                    })
                    .collect(Collectors.toList());

                return new Section(sectionId, sectionName, fields);
            })
            .collect(Collectors.toList());

        return new Question(questionName, questionId, sections);
    })
    .collect(Collectors.toList());

关键说明

  • 多级分组去重:通过两次groupingBy,先按QuestionId拆分数据,再在每个Question组内按SectionId拆分,确保每个Section仅被处理一次。
  • 避免重复实例:每个Section仅在其对应的分组下创建一次,再将该Section下的所有Field收集到List中,彻底解决重复Section问题。
  • Getter依赖:假设FlatQuestion类已实现对应的Getter方法(如getQuestionId()、getSectionName()等),若未实现需补充,否则无法通过Stream访问字段。
  • 字段校验(可选):若担心同一QuestionId下的QuestionName不一致,可提前通过Collectors.toMap统一名称:
    Map<Integer, String> questionNameMap = data.stream()
        .collect(Collectors.toMap(FlatQuestion::getQuestionId, FlatQuestion::getQuestionName, (oldVal, newVal) -> oldVal));
    
    构建Question时直接从该Map中获取名称,无需依赖分组内的第一个元素。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 02:57:15