如何用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统一名称:
构建Question时直接从该Map中获取名称,无需依赖分组内的第一个元素。Map<Integer, String> questionNameMap = data.stream() .collect(Collectors.toMap(FlatQuestion::getQuestionId, FlatQuestion::getQuestionName, (oldVal, newVal) -> oldVal));
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

