Java Streams-按两个条件求和的结果分组

J-A*_*lex 8 java java-8 java-stream

我有一份订单清单,应按两个条件进行分组。

Order_Id| Customer |    Date    | Amount |
   1    | "Sam"    | 2019-03-21 | 100    |
   2    | "Nick"   | 2019-03-21 | 102    |
   3    | "Dan"    | 2019-03-21 | 300    |
   4    | "Sam"    | 2019-04-21 | 400    |
   5    | "Jenny"  | 2019-04-21 | 220    |
   6    | "Jenny"  | 2019-04-12 | 330    |
Run Code Online (Sandbox Code Playgroud)

对于当前示例,应该找到每月总金额最高的买家:

{
  MARCH: { customer='Dan', amount=300 }, 
  APRIL: { customer='Jenny', amount=550 }
}
Run Code Online (Sandbox Code Playgroud)

我能够找到一个解决方案:

public class Main {

    public static void main(String[] args) {
        List<Order> orders = List.of(
                new Order(1L, "Sam", LocalDate.of(2019, 3, 21), 100L),
                new Order(2L, "Nick", LocalDate.of(2019, 3, 21), 102L),
                new Order(3L, "Dan", LocalDate.of(2019, 3, 21), 300L),
                new Order(4L, "Sam", LocalDate.of(2019, 4, 21), 400L),
                new Order(5L, "Jenny", LocalDate.of(2019, 4, 21), 220L),
                new Order(6L, "Jenny", LocalDate.of(2019, 4, 12), 330L)
        );

        solution1(orders);
    } 

    private static void solution1(List<Order> orders) {
        final Map<Month, Map<String, Long>> buyersSummed = new HashMap<>();

        for (Order order : orders) {
            Map<String, Long> customerAmountMap = buyersSummed.computeIfAbsent(order.getOrderMonth(), mapping -> new HashMap<>());
            customerAmountMap.putIfAbsent(order.getCustomer(), 0L);
            Long customerAmount = customerAmountMap.get(order.getCustomer());
            customerAmountMap.put(order.getCustomer(), customerAmount + order.getAmount());
        }

        final Map<Month, BuyerDetails> topBuyers = buyersSummed.entrySet().stream()
                .collect(
                        toMap(Entry::getKey, customerAmountEntry -> customerAmountEntry.getValue().entrySet().stream()
                                .map(entry -> new BuyerDetails(entry.getKey(), entry.getValue()))
                                .max(Comparator.comparingLong(BuyerDetails::getAmount)).orElseThrow())
                );

        System.out.println(topBuyers);
    }

}
Run Code Online (Sandbox Code Playgroud)

我使用的数据模型:

class BuyerDetails {
    String customer;
    Long amount;

    public BuyerDetails(String customer, Long amount) {
        this.customer = customer;
        this.amount = amount;
    }

    public String getCustomer() {
        return customer;
    }

    public Long getAmount() {
        return amount;
    }

}

class Order {

    Long id;
    String customer;
    LocalDate orderDate;
    Long amount;

    public Order(Long id, String customer, LocalDate orderDate, Long amount) {
        this.id = id;
        this.customer = customer;
        this.orderDate = orderDate;
        this.amount = amount;
    }

    public Long getId() {
        return id;
    }

    public String getCustomer() {
        return customer;
    }

    public LocalDate getOrderDate() {
        return orderDate;
    }

    public Month getOrderMonth() {
        return getOrderDate().getMonth();
    }

    public Long getAmount() {
        return amount;
    }
}
Run Code Online (Sandbox Code Playgroud)

问题:

有没有办法一口气解决上面的任务?

Nid*_*nan 4

尝试使用groupingBy,summingLongcomparingLong如下所示

Map<Month, BuyerDetails> topBuyers = orders.stream()
    .collect(Collectors.groupingBy(Order::getOrderMonth,
             Collectors.groupingBy(Order::getCustomer,
             Collectors.summingLong(Order::getAmount))))
    .entrySet().stream()
    .collect(Collectors.toMap(Map.Entry::getKey,
             order -> order.getValue().entrySet().stream()
            .max(Comparator.comparingLong(Map.Entry::getValue))
            .map(cust -> new BuyerDetails(cust.getKey(), cust.getValue())).get()));
Run Code Online (Sandbox Code Playgroud)

输出

{
  "MARCH": { "customer": "Dan", "amount": 300 }, 
  "APRIL": { "customer": "Jenny", "amount": 550 }
}
Run Code Online (Sandbox Code Playgroud)