繁体   English   中英

Java8 Stream 如何分组和组合具有相同字段的元素

[英]Java8 Stream how to groupingBy and combine elements with same fields

  • 我有一个包含不同 ID 和日期的订单列表。
  • 现在,我需要合并相同 ID 和日期的订单金额。

这个问题的核心可能是如何创建一个 Collector 来组合具有相同 ID 和日期的订单,结果是一个订单列表而不是一个 Map<Integer, List>。

还有其他方法可以简化此流程吗?

public class Order {
    private Integer id;
    private LocalDate date;
    private double amount;
    public void accept(Order other) {
        setId(other.getId());
        setDate(other.getDate());
        setAmount(getAmount() + other.getAmount());
    }
    public Order combine(Order other) {
        setId(other.getId());
        setDate(other.getDate());
        setAmount(getAmount() + other.getAmount());
        return this;
    }
}
    List<Order> result = new ArrayList<>();

    List<Order> orders = mockData();
    Map<Integer, List<Order>> collect = list.stream()
            .collect(groupingBy(Order::getId));

    collect.forEach((id, orders) -> {
        Map<LocalDate, Order> resultMap = orders.stream()
                .collect(groupingBy(Order::getDate, mapping(order -> order, Collector.of(Order::new, Order::accept, Order::combine))));
        result.addAll(resultMap.values());
    });

我会先创建一个新记录,以便您可以同时使用 id 和 date。

record IdAndDate(Integer id, LocalDate date) {}

要不获取List<Order>作为地图的值类型,请使用toMap收集器。 然后您可以指定一个“合并功能”。 那就是您指定Order::combine的地方。

var result = new ArrayList<>(
    orders.stream().collect(
        Collectors.toMap(
            x -> new IdAndDate(x.getId(), x.getDate()), // key mapper
            Function.identity(), // value mapper (no change)
            Order::combine // merge function
        )
    ).values()
);

请注意, Order.combine更改了调用它的实例,这意味着原始列表中的某些订单将被此操作更改。 这对于您的原始代码也是如此,所以我假设这个事实对您的情况无关紧要。 以防万一您不希望这种情况发生,您应该让combine返回一个新的Order实例,而不是this

您还可以嵌套 groupingBy 调用并执行以下操作:

List<Order> orders = mockData();

List<Order> result = orders.stream()   // Stream<Order>
                           .collect(Collectors.groupingBy(Order::getId,
                                      Collectors.groupingBy(Order::getDate)))   //Map<Integer, Map<LocalDate,List<Order>>>
                          .values()   //Collection<Map<LocalDate,List<Order>>>
                          .stream()    ////Stream<Map<LocalDate,List<Order>>>
                          .flatMap(m -> m.values().stream())    //Stream<List<Order>>
                          .map(list -> list.stream().reduce(Order::combine).get())     // Stream<Order>
                          .collect(Collectors.toList());

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM