首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用Java流对嵌套对象进行分组和排序

使用Java流对嵌套对象进行分组和排序
EN

Stack Overflow用户
提问于 2022-05-17 16:36:09
回答 3查看 556关注 0票数 2

我有一个带有嵌套list字段的DTO对象列表。

其目的是按照id字段和merge对它们进行分组,然后使用Streams API对列表进行排序。

代码语言:javascript
运行
复制
class DTO {
    private Long id;
    private List<ItemDTO> items;
}

class ItemDTO {
    private Long priority;
    private Long value;
}

// input
List<DTO> dtoList = List.of(
 DTO(1, List.of(ItemDTO(1, 1), ItemDTO(7, 2))),
 DTO(2, List.of(ItemDTO(1, 1), ItemDTO(2, 2))),
 DTO(1, List.of(ItemDTO(10, 3), ItemDTO(1, 4)))
);

我需要使用相同的id字段对这些嵌套对象进行分组,并按字段priority合并降序中的所有项。

这个dtoList的最终结果如下所示:

代码语言:javascript
运行
复制
// output 
List<DTO> resultList = [
        DTO(1, List.of(ItemDTO(10,3), ItemDTO(7,2), ItemDTO(1,1), ItemDTO(1,4)),
        DTO(2, List.of(ItemDTO(2,2), ItemDTO(1,1),
    ];

我们能用Streams API实现这一点吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-05-17 17:09:12

我会从一个简单的分组开始,以获得一个映射Map<Long,List<DTO>>,并在该映射的条目上进行流,并将每个映射映射到一个新的DTO。您可以提取一个方法/函数来对ItemDTO进行排序:

代码语言:javascript
运行
复制
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

....


Function<List<DTO>, List<ItemDTO>> func =
        list -> list.stream()
                .map(DTO::getItems)
                .flatMap(List::stream)
                .sorted(Comparator.comparing(ItemDTO::getPriority,Comparator.reverseOrder()))
                .collect(Collectors.toList());

List<DTO> result = 
        dtoList.stream()
               .collect(Collectors.groupingBy(DTO::getId))
               .entrySet().stream()
               .map(entry -> new DTO(entry.getKey(), func.apply(entry.getValue())))
               //.sorted(Comparator.comparingLong(DTO::getId)) if the resulting list need to be sorted by id
               .collect(Collectors.toList());
票数 3
EN

Stack Overflow用户

发布于 2022-05-17 17:00:29

您可以通过将数据按id分组来创建中间映射,然后将每个条目转换为一个新的DTO对象。

为此,您可以使用内置收集器groupingBy()flatMapping()的组合来创建中间映射。

为了对每个id映射的项进行排序,flatMapping()collectionAndThen()一起使用。

代码语言:javascript
运行
复制
public static void main(String[] args) {
    // input
    List<DTO> dtoList = List.of(
        new DTO(1L, List.of(new ItemDTO(1L, 1L), new ItemDTO(7L, 2L))),
        new DTO(2L, List.of(new ItemDTO(1L, 1L), new ItemDTO(2L, 2L))),
        new DTO(1L, List.of(new ItemDTO(10L, 3L), new ItemDTO(1L, 4L)))
    );
    
    List<DTO> result = dtoList.stream()
        .collect(Collectors.groupingBy(DTO::getId,
            Collectors.collectingAndThen(
            Collectors.flatMapping(dto -> dto.getItems().stream(), Collectors.toList()),
                (List<ItemDTO> items) -> {
                    items.sort(Comparator.comparing(ItemDTO::getPriority).reversed());
                    return items;
            })))
        .entrySet().stream()
        .map(entry -> new DTO(entry.getKey(), entry.getValue()))
        .collect(Collectors.toList());
    
    result.forEach(System.out::println);
}

输出

代码语言:javascript
运行
复制
DTO{id = 1, items = [ItemDTO{10, 3}, ItemDTO{7, 2}, ItemDTO{1, 1}, ItemDTO{1, 4}]}
DTO{id = 2, items = [ItemDTO{2, 2}, ItemDTO{1, 1}]}

正如@shmosel所指出的,flatMapping()是Java9的优点之一。您可能也会认为它是一个提醒,也许是时候转向Java9提供的模块化系统和其他有用的特性了。

完全符合Java 8的版本如下所示:

代码语言:javascript
运行
复制
List<DTO> result = dtoList.stream()
    .collect(Collectors.groupingBy(DTO::getId,
        Collectors.collectingAndThen(
            Collectors.mapping(DTO::getItems, Collectors.toList()),
                (List<List<ItemDTO>> items) ->
                    items.stream().flatMap(List::stream)
                        .sorted(Comparator.comparing(ItemDTO::getPriority).reversed())
                        .collect(Collectors.toList())
                    )))
            .entrySet().stream()
            .map(entry -> new DTO(entry.getKey(), entry.getValue()))
            .collect(Collectors.toList());
票数 2
EN

Stack Overflow用户

发布于 2022-05-17 17:32:55

这是最简单的方法。我假定您已经为您的类定义了适当的getter。

  • 简单地隐藏在id.
  • 上的地图上,合并适当的列表
  • 并返回值并转换为ArrayList.

代码语言:javascript
运行
复制
List<DTO> results = new ArrayList<>(dtoList.stream().collect(
        Collectors.toMap(DTO::getId, dto -> dto, (a, b) -> {
            a.getItems().addAll(b.getItems());
            return a;
        })).values());

然后根据您的需求对它们进行排序。这不需要更多的时间来做它的溪流构造,但在我看来,是不那么杂乱。

代码语言:javascript
运行
复制
for (DTO d: results) {   
    d.getItems().sort(Comparator.comparing(ItemDTO::getPriority)
       .reversed());
}

results.forEach(System.out::println);

打印(对这两个类使用简单的toString )

代码语言:javascript
运行
复制
DTO[1, [ItemDTO[10, 3], ItemDTO[7, 2], ItemDTO[1, 1], ItemDTO[1, 4]]]
DTO[2, [ItemDTO[2, 2], ItemDTO[1, 1]]]

注意:List.of是不可变的,所以您不能更改它们。我会在您的列表构造中使用new ArrayList<>(List.of(...))

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72277722

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档