我有如下集合元素的列表。我想要groupBy多个字段,比如productCode,然后用于产品购买类型。
class Product{
private String productCode;
// this is ENUM with 2 possible values "ONLINE" or "INSTORE"
private String productPurchaseType;
private String productCost;
...
}
可能的输出应该类似于
ROW1::ProductCode1, Count of ONLINE,Count of INSTORE,Min, Max
ROW2::ProductCode2, Count of ONLINE,Count of INSTORE, Min, Max
我使用了下面的代码,但是它没有给出在线和安装的计数
void groupByMerchantMCCCodeZIP(List<Product> productList) {
Map<String, Map<String, List<Product>>> output = transactionDataList.stream()
.collect(Collectors.groupingBy(Product::getProductCode,
Collectors.groupingBy(Product::productPurchaseType)));
System.out.println(output);
}
某个在java8groupby上工作的人能让我知道做这件事的最佳方法吗?
谢谢!
发布于 2018-06-28 03:33:11
要聚合多个值,您应该编写自己的Collector
,以获得最佳性能。
编写Collector
的最简单方法是调用Collector.of()
方法,并结合一个结果收集器类。下面是一个例子。
首先,我重新定义了Product
,使其具有更好的字段类型:
class Product {
public enum PurchaseType { ONLINE, INSTORE }
private final String code;
private final PurchaseType purchaseType;
private final BigDecimal cost;
public Product(String code, PurchaseType purchaseType, String cost) {
this.code = code;
this.purchaseType = purchaseType;
this.cost = new BigDecimal(cost);
}
public String getCode() {
return this.code;
}
public PurchaseType getPurchaseType() {
return this.purchaseType;
}
public BigDecimal getCost() {
return this.cost;
}
}
然后,结果收集器类:
class ProductResult {
private int onlineCount;
private int instoreCount;
private BigDecimal minCost;
private BigDecimal maxCost;
public void add(Product product) {
if (product.getPurchaseType() == Product.PurchaseType.ONLINE)
this.onlineCount++;
else if (product.getPurchaseType() == Product.PurchaseType.INSTORE)
this.instoreCount++;
if (this.minCost == null || product.getCost().compareTo(this.minCost) < 0)
this.minCost = product.getCost();
if (this.maxCost == null || product.getCost().compareTo(this.maxCost) > 0)
this.maxCost = product.getCost();
}
public ProductResult merge(ProductResult that) {
this.onlineCount += that.onlineCount;
this.instoreCount += that.instoreCount;
if (this.minCost == null || that.minCost.compareTo(this.minCost) < 0)
this.minCost = that.minCost;
if (this.maxCost == null || that.maxCost.compareTo(this.maxCost) > 0)
this.maxCost = that.maxCost;
return this;
}
@Override
public String toString() {
return "[online: " + this.onlineCount +
", instore: " + this.instoreCount +
", min: " + this.minCost +
", max: " + this.maxCost + "]";
}
public int getOnlineCount() {
return this.onlineCount;
}
public int getInstoreCount() {
return this.instoreCount;
}
public BigDecimal getMinCost() {
return this.minCost;
}
public BigDecimal getMaxCost() {
return this.maxCost;
}
}
演示
List<Product> productList = Arrays.asList(
new Product("MILK", Product.PurchaseType.ONLINE, "3.99"),
new Product("MILK", Product.PurchaseType.ONLINE, "3.99"),
new Product("MILK", Product.PurchaseType.INSTORE, "4.95"),
new Product("BREAD", Product.PurchaseType.INSTORE, "7.48")
);
Map<String, ProductResult> result = productList.stream()
.collect(Collectors.groupingBy(Product::getCode,
Collector.of(ProductResult::new,
ProductResult::add,
ProductResult::merge,
Characteristics.UNORDERED)));
result.entrySet().forEach(System.out::println);
输出
BREAD=[online: 0, instore: 1, min: 7.48, max: 7.48]
MILK=[online: 2, instore: 1, min: 3.99, max: 4.95]
https://stackoverflow.com/questions/51073787
复制相似问题