Java 流 - 基于子 class 的分组并从父 class 计算总和

Java Stream - Groupby based on child class and calculate sum from parent class

以下是我的实体:

产品

@Entity
@Table(name = "Product")
public class Product extends ReusableFields
{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    Long productId;

    @NonNull
    @Column(name = "product_name")
    String productName;
    String measurementUnit;
    //more fields and getters setters
}

与产品相关的进出货清单:

@Entity
@Table(name = "inward_outward_entries")
public class InwardOutwardList extends ReusableFields
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long entryid;

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "productId", nullable = false)
    @JsonIgnoreProperties(
    { "hibernateLazyInitializer", "handler" })
    Product product;
    
    @JsonSerialize(using = DoubleTwoDigitDecimalSerializer.class)
    Double quantity;
    //more fields
}

进出库清单有一套:

@Entity
@Table(name = "inward_inventory")
public class InwardInventory extends ReusableFields implements Cloneable
{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "inwardid")
    Long inwardid;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "inwardinventory_entry", joinColumns =
    { @JoinColumn(name = "inwardid", referencedColumnName = "inwardid") }, inverseJoinColumns =
    { @JoinColumn(name = "entryId", referencedColumnName = "entryId") })
    Set<InwardOutwardList> inwardOutwardList = new HashSet<>();

    //more fields

}

我有一份入库清单,我想根据产品对其进行分组。所以,我想做的是

SUM(InwardInventory.InwardOutwardList.quantity) 根据 InwardInventory.InwardOutwardList.Product.productName 和 InwardInventory.InwardOutwardList.Product.measurementUnit

分组

我是流的新手,我知道它可以完成,但无法获得确切的解决方案。有人可以指导或帮助吗。

据我了解,您想要一个 Map,其中键是唯一 productNamemeasurementUnit 的某种表示,值是双精度。

首先,您需要定义将用作地图键的 class。它的要求是仅基于 productNamemeasurementUnit 字段实现 equals 和 hashCode。否则,您将无法将数量正确地聚合到地图中。如果你遵循 Vlad Mihalcea's advice 那么你不能使用 Product class 作为键,因为它仅基于 id 字段实现 equals 和 hashCode。在下面的示例中,Pair<L, R> 已根据 LR 字段正确实施了 equals 和 hashCode。

List<InwardInventory> inwardInventoryList = ...;

Map<Pair<String, String>, Double> map = inwardInventoryList.stream()
        .flatMap(i -> i.getInwardOutwardList().stream())
        .collect(Collectors.toMap(l -> Pair.of(l.getProduct().getProductName(), l.getProduct().getMeasurementUnit()),
                InwardOutwardList::getQuantity,
                Double::sum));

第一个操作是 flatMap,因为每个 InwardInventory 都有一个 Set<InwardOutwardList>,但我们想要单个 InwardOutwardList 对象流。

第二个操作是Collectors.toMap定义的here.

我根据您的代码创建了简单的 classes,因为不需要注释。

Class Product

public class Product {
    private Long  productId;
    private String  measurementUnit;
    private String  productName;

    public Product(Long productId, String measurementUnit, String productName) {
        this.productId = productId;
        this.measurementUnit = measurementUnit;
        this.productName = productName;
    }

    public Long getProductId() {
        return productId;
    }

    public String getMeasurementUnit() {
        return measurementUnit;
    }

    public String getProductName() {
        return productName;
    }
}

Class InwardOutwardList

public class InwardOutwardList {
    private Long  entryid;
    private Product  product;
    private Double quantity;

    public InwardOutwardList(Long entryid, Product product, Double quantity) {
        this.entryid = entryid;
        this.product = product;
        this.quantity = quantity;
    }

    public Long getEntryid() {
        return entryid;
    }

    public Product getProduct() {
        return product;
    }

    public Double getQuantity() {
        return quantity;
    }
}

最后,class InwardInventory 其中包含方法 main,演示了如何使用流 API 来实现您想要的结果。
(代码后的注释。)

import java.util.DoubleSummaryStatistics;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class InwardInventory {
    private Long  inwardid;
    private Set<InwardOutwardList>  inwardOutwardList = new HashSet<>();

    public InwardInventory(Long id) {
        inwardid = id;
    }

    public static void main(String[] args) {
        Product p1 = new Product(1L, "unit", "Product_1");
        Product p2 = new Product(1L, "unit", "Product_1");
        InwardOutwardList ioLst = new InwardOutwardList(1L, p1, 1D);
        InwardOutwardList ioLst2 = new InwardOutwardList(2L, p2, 2D);
        InwardInventory ii = new InwardInventory(1L);
        ii.inwardOutwardList.add(ioLst);
        ii.inwardOutwardList.add(ioLst2);
        Map<String, DoubleSummaryStatistics> map = ii.inwardOutwardList.stream()
                                                     .collect(Collectors.groupingBy(iol -> iol.getProduct().getProductName(),
                                                                                    Collectors.summarizingDouble(InwardOutwardList::getQuantity)));
        map.forEach((p, s) -> System.out.println(p + " = " + s.getSum()));
    }
}
  • ii.inwardOutwardList.stream() 创建一个 Stream,其中该流中的每个元素都是 InwardOutwardList.
  • 的实例
  • collect method has a single argument whose type is Collector.
  • Class Collectors 是一个实用程序 class,它包含 return 特殊收集器的方法。
  • collect 方法 returns a Map 其中映射键是产品名称 – 从 class Product 中提取,映射值是一个class DoubleSummaryStatistics.
  • 的实例
  • 方法getSum,在classDoubleSummaryStatisticsreturns中,本例为数量之和。

运行 以上代码产生以下结果:

Product_1 = 3.0