![](/img/trans.png)
[英]How to group values from a list with Java Stream API (groupingBy collector)?
[英]Nesting Values with Java 8 stream and collector
我有以下数据集来表示销售记录:
sn| Channel | Category | Brand |qty | gross |
1 |"Mini Market" | "Large MM" | "ARIEL" |3 | 100 |
2 |"Mini Market" | "Large MM" | "ARIEL" |6 | 200|
3 |"Mini Market" | "Large MM" | "GILLETTE" |12 | 103.88|
4 |"Mini Market" | "Large MM" | "OLAY" |2 | 50 |
5 |"Mini Market" | "Large MM" | "OLAY" |6 | 10|
6 |"Mini Market" | "Small MM" | "GILLETTE" |5 | 20 |
7 |"Mini Market" | "Small MM" | "GILLETTE" |3 | 30|
8 |"Mini Market" | "Small MM" | "OLAY" |3 | 80.3 |
9 |"Mini Market" | "Small MM" | "ORAL B" |6 | 100 |
10|"Mini Market" | "Small MM" | "ORAL B" |7 | 150 |
POJO 类:
class SalesRecord{
private String channel;
private String category;
private String brand;
private int qty;
private double gross;
//getters and setters
}
class PivotTable {
Map<Integer,Set<String>> uniqueAttirbuteMap;
List<Pivot> pivot;
//other fields and methods
}
class Pivot {
public String attribute;
Map<String, Double> aggregates;
List<Pivot> pivotList;
//other fields and methods
}
自定义收集器类:
public class SalesCollector implements Collector<SalesRecord,
SalesCollector.Accumulator, PivotTable> {
private static final String GROSS_SUM = "sum_gross";
private static final String QTY_SUM = "sum_qty"
public List<Double> prices = Lists.newArrayList();
public List<Double> qtys = Lists.newArrayList();
public double totalSales = 0;
public double totalQty = 0;
public SalesCollector(final List<GroupBy> groups) {
this.groups = groups;
}
@Override
public Supplier<Accumulator> supplier() {
return () -> new Accumulator(this.groups, this);
}
@Override
public BiConsumer<Accumulator, SalesRecord> accumulator() {
return Accumulator::accumulate;
}
@Override
public BinaryOperator<Accumulator> combiner() {
return Accumulator::combine;
}
@Override
public Function<Accumulator, PivotTable> finisher() {
return Accumulator::toSummary;
}
static class Accumulator {
private final List<GroupBy> groups;
private final SalesCollector collector;
PivotTable pivotTable = new PivotTable();
Accumulator(final List<GroupBy> groups, final SalesCollector collector) {
this.groups = groups;
this.collector = collector;
}
void accumulate(SalesRecord elem) {
double sum = pivotTable.getAggregates().getOrDefault(GROSS_SUM, 0D) + elem.getGross();
pivotTable.getAggregates().put(GROSS_SUM, sum);
double qtySum = pivotTable.getAggregates().getOrDefault(QTY_SUM, 0D) + elem.getQty();
pivotTable.getAggregates().put(QTY_SUM, qtySum);
}
Accumulator combine(Accumulator other) {
final PivotTable summary = other.toSummary();
double sum_qty =
pivotTable.getAggregates().get(QTY_SUM) + summary.getAggregates().get(QTY_SUM);
pivotTable.getAggregates().put(QTY_SUM, sum_qty);
double sum_gross =
pivotTable.getAggregates().get(GROSS_SUM) + summary.getAggregates().get(GROSS_SUM);
pivotTable.getAggregates().put(GROSS_SUM, sum_gross);
return this;
}
PivotTable toSummary() {
double sumGross = pivotTable.getAggregates().get(GROSS_SUM);
collector.prices.add(sumGross);
collector.totalSales += sumGross;
double sumQty = pivotTable.getAggregates().get(QTY_SUM);
collector.qtys.add(sumQty);
collector.totalQty += sumQty;
return pivotTable;
}
}
}
目前我正在使用流和 gropuing 并有一个自定义收集器来计算 qty 和 total 的值,如下所示:
SalesCollector collector = new SalesCollector(groups);
final Map<String, Map<String, Map<String, PivotTable>>> results = salesRecords.stream()
.collect(groupingBy(SalesRecord::getChannel(), TreeMap::new,
groupingBy(SalesRecord::getCategoryName(), TreeMap::new,
groupingBy(SalesRecord::getBrand(), TreeMap::new, collector))));
List<PivotTable> myList = results.values().stream()
.map(Map::values)
.flatMap(Collection::stream)
.map(Map::values)
.flatMap(Collection::stream)
.collect(Collectors.toList());
我目前的结果如下:
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:ARIEL, aggregates:{ sum_qty=9, sum_gross=300 })])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:GILLETTE, aggregates:{ sum_qty = 12, sum_gross= 103.88})])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:OLAY, aggregates:{ sum_qty = 8, sum_gross= 60})])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:OLAY, aggregates:{ sum_qty = 3, sum_gross= 80.3})])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates:{ sum_qty = 8, sum_gross= 50})])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:ORAL B, aggregates:{ sum_qty = 13, sum_gross= 250})])
我想要实现的目标如下:
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), pivotList:[Pivot(attribute:ARIEL, aggregates:{ sum_qty=9, sum_gross=300 }),
Pivot(attribute:GILLETTE, aggregates:{ sum_qty = 12, sum_gross= 103.88}),Pivot(attribute:OLAY, aggregates:{ sum_qty = 8, sum_gross= 60})])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates:{ sum_qty = 8, sum_gross= 50})]),
Pivot(attribute:OLAY, aggregates:{ sum_qty = 3, sum_gross= 80.3}),Pivot(attribute:ORAL B, aggregates:{ sum_qty = 13, sum_gross= 250})])
简单地说,我想嵌套如下:
Mini Market:
Large MM:
ARIEL: {sum_qty = 9, sum_gross= 300}
GILLETTE: {sum_qty = 12, sum_gross= 103.88}
OLAY: {sum_qty = 8, sum_gross= 60}
Mini Market:
Small MM:
GILLETTE: {sum_qty = 8, sum_gross= 50}
OLAY: {sum_qty = 3, sum_gross= 80.3}
ORAL B: {sum_qty = 13, sum_gross= 250}
是否可以使用相同分组的电流收集器结果本身来实现这一点? 实现这一目标的最佳方法是什么?
我通过丢失的collector
扩展了您的流。 它是一个专用的Collector
,它将每个SalesRecord
映射到一个Map<String, Double>
。 由于Map
有一种值类型,我决定使用Double
。
Supplier<Map<String, Double>> supplier = TreeMap::new;
BiConsumer<Map<String, Double>, SalesRecord> biConsumer = (map, sr) -> {
map.merge("sum_qty", Double.valueOf(sr.getQty()), (qtySum, qty) -> qtySum + qty);
map.merge("sum_gross", sr.getGross(), (grossSum, gross) -> grossSum + gross);
};
BinaryOperator<Map<String, Double>> binaryOperator = (l, r) -> {
l.compute("sum_qty", (k, v) -> v + r.get("sum_qty"));
l.compute("sum_gross", (k, v) -> v + r.get("sum_gross"));
return l;
};
Collector<SalesRecord, Map<String, Double>, Map<String, Double>> collector = Collector.of(supplier, biConsumer, binaryOperator);
现在作为最后一个下游收集器添加到流中:
Map<String, Map<String, Map<String, Map<String, Double>>>> grouped = salesRecords.stream().collect(Collectors.groupingBy(
SalesRecord::getChannel, TreeMap::new,
Collectors.groupingBy(SalesRecord::getCategory, TreeMap::new,
Collectors.groupingBy(SalesRecord::getBrand, collector))));
结果是一个Map<String, Map<String, Map<String, Map<String, Double>>>> grouped
并且看起来像这样(为了可读性添加了换行符):
{Mini Market=
{Large MM={
OLAY={sum_gross=60.0, sum_qty=8.0},
ARIEL={sum_gross=300.0, sum_qty=9.0},
GILLETTE={sum_gross=103.88, sum_qty=12.0}},
Small MM={
OLAY={sum_gross=80.3, sum_qty=3.0},
ORAL B={sum_gross=250.0, sum_qty=13.0},
GILLETTE={sum_gross=50.0, sum_qty=8.0}}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.