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
,其中键是唯一 productName
和 measurementUnit
的某种表示,值是双精度。
首先,您需要定义将用作地图键的 class。它的要求是仅基于 productName
和 measurementUnit
字段实现 equals 和 hashCode。否则,您将无法将数量正确地聚合到地图中。如果你遵循 Vlad Mihalcea's advice 那么你不能使用 Product
class 作为键,因为它仅基于 id
字段实现 equals 和 hashCode。在下面的示例中,Pair<L, R>
已根据 L
和 R
字段正确实施了 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
,在classDoubleSummaryStatistics
returns中,本例为数量之和。
运行 以上代码产生以下结果:
Product_1 = 3.0
以下是我的实体:
产品
@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
,其中键是唯一 productName
和 measurementUnit
的某种表示,值是双精度。
首先,您需要定义将用作地图键的 class。它的要求是仅基于 productName
和 measurementUnit
字段实现 equals 和 hashCode。否则,您将无法将数量正确地聚合到地图中。如果你遵循 Vlad Mihalcea's advice 那么你不能使用 Product
class 作为键,因为它仅基于 id
字段实现 equals 和 hashCode。在下面的示例中,Pair<L, R>
已根据 L
和 R
字段正确实施了 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 aMap
其中映射键是产品名称 – 从 classProduct
中提取,映射值是一个classDoubleSummaryStatistics
. 的实例
- 方法
getSum
,在classDoubleSummaryStatistics
returns中,本例为数量之和。
运行 以上代码产生以下结果:
Product_1 = 3.0