使用 lambda 根据列表的 Id 对列表中的对象进行分组

Posted

技术标签:

【中文标题】使用 lambda 根据列表的 Id 对列表中的对象进行分组【英文标题】:Use lambda to group an object within a list according to the Id of the list 【发布时间】:2022-01-04 05:04:30 【问题描述】:

晚安,我需要一些帮助来使用 Java 8 (lambda) 对对象列表进行分组

我有我的实体 QuestionEventDTO

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class QuestionEventDTO 
    
    public Long id;
    public QuestionDTO question;
    public List<QuestionDTO> questions;


当我咨询银行时,我得到了这样的外向回复:

QuestionEventDTO [id=1, question= QuestionDTO [id=1, description="Are you Dev"], questions =[]]
QuestionEventDTO [id=1, question= QuestionDTO [id=2, description="What's rating?"], questions = []]
QuestionEventDTO [id=2, question= QuestionDTO [id=3, description="What's your dog's name?"], questions = []]
QuestionEventDTO [id=2, questao= QuestionDTO [id=4, description="Are you Dev?"], questions = []]

但是,请注意 EventQuestion_id 是重复的,只有对象 QuestionDto 具有不同的 id。我想将 QuestionsDto 分组到一个 QuestionEvent 中,如下所示:

QuestionEventDTO [id=1, question= null, questions =[ QuestaoDTO [id=1, description="Are you Dev"], QuestaoDTO [id=2, description="What's rating?"] ] ]
QuestionEventDTO [id=2, question= null, questions =[ QuestaoDTO [id=3, description="What's your dog's name?"], QuestaoDTO [id=4, description="Are you Dev?"] ] ]

有人可以帮助我使用 lambda 找到一个好的解决方案吗?它是快速和高效的吗?谢谢。

【问题讨论】:

【参考方案1】:

您可以尝试像这样添加注释 @EqualsAndHashCode(exclude = "question", "questions")

【讨论】:

【参考方案2】:

我认为这不是最好的方法,但我正在分享你想要的结果。

allList.stream()
            .collect(Collectors.groupingBy(QuestionEventDTO::getId))
            .entrySet()
            .stream()
            .map(e -> 
                QuestionEventDTO newInstance = new QuestionEventDTO();
                newInstance.setId(e.getKey());
                newInstance.setQuestions(e.getValue().stream().map(v -> v.getQuestion()).collect(Collectors.toList()));
                return newInstance;
            ).collect(Collectors.toList());

【讨论】:

【参考方案3】:

这里需要将QuestionEventDTO 重新映射到新实例,并将question 字段设置为null,并填充列表。

这可以使用带有合并功能的Collectors.toMap 来实现(和Supplier&lt;Map&gt; 提供LinkedHashMap 并保持插入顺序):

List<QuestionEventDto> questionEvents; // input list
List<QuestionEventDto> groupedById = new ArrayList<>(questionEvents
    .stream() // Stream<QuestionEventDto>
    .collect(Collectors.toMap(
        QuestionEventDto::getId, // key
        qe -> new QuestionEventDto(
            qe.getId(), null, new ArrayList(Arrays.asList(qe.getQuestion()))
        ), // use all-args constructor
        (qe1, qe2) ->  
            qe1.getQuestions().addAll(qe2.getQuestions()); 
            return qe1;
        ,
        LinkedHashMap::new // keep insertion order
    )) // Map<Long, QuestionEventDto> created
    .values() // Collection<QuestionEventDto>
); // convert to ArrayList<QuestionEventDto>

另一种解决方案可能是使用Collectors.groupingBy + Collectors.mapping

List<QuestionEventDto> questionEvents; // input list
List<QuestionEventDto> groupedById = questionEvents
    .stream() 
    .collect(Collectors.groupingBy(
        QuestionEventDto::getId, // key
        LinkedHashMap::new,
        Collectors.mapping(
            QuestionEventDto::getQuestion, Collectors.toList()
        ) // value - List<QuestionDto>
    ))
    .entrySet()
    .stream() // Stream<Map.Entry<Long, List<QuestionDto>>>
    .map(e -> new QuestionEventDto(e.getKey(), null, e.getValue()))
    .collect(Collectors.toList());

【讨论】:

以上是关于使用 lambda 根据列表的 Id 对列表中的对象进行分组的主要内容,如果未能解决你的问题,请参考以下文章

将哈希表转换为 OCaml 中的对列表(键、值)

如何从 Erlang 中的对列表中获取元素

使用lambda表达式按字段排序

使用“id”字段更新列表的子部分

如何使用linq / lambda计算具有唯一属性的列表中的对象数?

django - 使用 lambda 进行 OR 查询