繁体   English   中英

收集器映射() + 减少() - 折叠 stream 元素与 Java 具有一对多关系 8

[英]Collectors mapping() + reducing() - folding stream element having One-To-Many relationship with Java 8

我有以下域类的类:

@AllArgsConstructor
@Getter
@ToString
public static class Customer {
    private String id;
    private String name;
}

@Getter
public static class Order {
    private String id;
    private Set<OrderContent> orderContent = new HashSet<>();
    private Customer customer;
    
    public Order(String id, Customer customer) {
        this.id = id;
        this.customer = customer;
    }
    
    public Order addOrderContent(Product product, int amount) {
        orderContent.add(new OrderContent(product, amount));
        return this;
    }
}

@AllArgsConstructor
@Getter
public static class Product {
    private String name;
    private BigDecimal price;
}

@AllArgsConstructor
@Getter
public static class OrderContent {
    private Product product;
    private Integer quantity;
}

因此,每个Order都与一个Customer关联,并与OrderContent一对多关联。 每个OrderContent都有属性: productquantity

我想退回最有价值的客户,也就是所有下中价值最高的客户

让我们考虑下面的例子:

List<Order> orders = new ArrayList<>();

Customer customer1 = new Customer("1", "Bill");
Customer customer2 = new Customer("2", "John");

Product device1 = new Product("Device 1", new BigDecimal("30.00"));
Product device2 = new Product("Device 2", new BigDecimal("50.00"));
Product device3 = new Product("Device 3", new BigDecimal("110.00"));

orders.add(new Order("1", customer1).addOrderContent(device1 , 1));
orders.add(new Order("2", customer1).addOrderContent(device2 , 1));
orders.add(new Order("3", customer2).addOrderContent(device3, 1));

下面的代码无法编译。 问题出在收集器内部,因为mapping()中 arguments 的类型不匹配。 如何正确实现这个逻辑?

我的代码:

Customer customer = orders.stream()
    .collect(Collectors.groupingBy(Order::getCustomer,
        Collectors.mapping((Order order) -> order.getOrderContent().stream()
            .map(orderContent -> orderContent.getProduct().getPrice()
                .multiply(new BigDecimal(orderContent.getQuantity()))),
            Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))
    ))
    .entrySet().stream()
    .max(Comparator.comparing(Map.Entry::getValue))
    .map(Map.Entry::getKey).orElse(null);

你快到了。

唯一需要做的是正确处理一对多的转换——一个订单到这个订单中的产品价格

Java 9 +

代替收集器mapping()Order object 转换为OrderContent对象序列,您可以使用从 JDK 版本 9 开始可用的收集器flatMapping()作为groupingBy()的下游。

然后我们可以通过将 price 和 amount 相乘将每个OrderContent转换为BigDecimal ,并像您一样使用 reduce reducing()获得每个Customer的总花费。

Customer mostValCust = orders.stream()
    .collect(Collectors.groupingBy(Order::getCustomer,
        Collectors.flatMapping((Order order) -> order.getOrderContent().stream()
            .map(orderContent -> orderContent.getProduct().getPrice()
                .multiply(BigDecimal.valueOf(orderContent.getQuantity()))),
        Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))
    ))
    .entrySet().stream()
    .max(Map.Entry.comparingByValue())
    .map(Map.Entry::getKey)
    .orElse(null);

Java 8

用 Java 8 实现它的方法之一是将累积每个Order的整体价格的逻辑移到收集器reduction reducing()中:

Customer mostValCust = orders.stream()
    .collect(Collectors.groupingBy(Order::getCustomer,
        Collectors.reducing(
            BigDecimal.ZERO,
            order -> order.getOrderContent().stream()
                .map(orderContent -> orderContent.getProduct().getPrice()
                    .multiply(BigDecimal.valueOf(orderContent.getQuantity())))
                .reduce(BigDecimal.ZERO,BigDecimal::add),
        BigDecimal::add)
    ))
    .entrySet().stream()
    .max(Map.Entry.comparingByValue())
    .map(Map.Entry::getKey)
    .orElse(null);

暂无
暂无

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

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