按对象数组列表分组而不创建地图
Group by arraylist of objects without creating map
我有对象 PayBill,其中包含项目列表。对象项目包含文章。本文连接到另一个 Nomenclature 对象,该对象存储为 db table,下一个结构为:
Article ProductGroup
------- ------------
010101 Telephone
040444 Computer
我将我的交易列表解析为具有以下属性的对象 Stat:payBillNumber
、productGroup
、amountOfItems
、sumPriceOfItems
。现在 Stat
的列表如下所示:
BillNumber ProductGroup NumberOfItemsInBill SummaryItemsCostInBill
---------- ------------ ------------------- ----------------------
1 Telephone 1 1000
1 Computer 1 200
1 Telephone 2 2000
1 Computer 1 200
2 Accessories 1 1500
如何使用以下视图将此 List<Stat>
折叠成新的 List<Stat>
:
BillNumber ProductGroup NumberOfItemsInBill SummaryItemsCostInBill
---------- ------------ ------------------- ----------------------
1 Telephone 3 3000
1 Computer 2 400
2 Accessories 1 1500
我希望我的数据按 BillNumber 和 ProductGroup 进行分组,金额与价格相加。是否可以不创建新地图?
我想出了一个涉及中间地图的解决方案,但以实用的方式使用 groupingBy
和 reducing
:
List<Stat> collect = stats.stream()
.collect(Collectors.groupingBy(Stat::getBillNumber, Collectors.groupingBy(Stat::getProductGroup,
Collectors.reducing((left, right) -> new Stat(
left.getBillNumber(),
left.getProductGroup(),
left.getNumberOfItemsInBill() + right.getNumberOfItemsInBill(),
left.getSummaryItemsCostInBill() + right.getSummaryItemsCostInBill()
)))))
.values().stream()
.flatMap(m -> m.values().stream())
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
请注意,这会导致创建大量 Stat
个对象。如果性能是一个问题,你可以想出一些聚合器并使用 Collectors.of
而不是 Collectors.reducing
或者坚持使用 reducing
并在最后映射它。
您必须创建 Stat
构造函数,但它是这样的:
Collection<Stat> aggregated = stats.stream()
.collect(Collectors.toMap(
stat -> new AbstractMap.SimpleEntry<>(stat.getProductGroup(), stat.getBillNumber()),
stat -> stat,
(stat1, stat2) -> new Stat(stat1.getBillNumber(), stat1.getProductGroup(), stat1.getNumberOfItemsInBill() + stat2.getNumberOfItemsInBill(), stat1.getSummaryItemsCostInBill() + stat2.getSummaryItemsCostInBill()
))
.values();
我创建了下面的 class 来表示排序形式的数据。
@Data
class Stat{
int bn;
String pg;
int noOfBills;
int cost;
public Stat(int bn, String pg, int noOfBills, int cost)
{
this.bn = bn;
this.pg = pg;
this.noOfBills = noOfBills;
this.cost = cost;
}
@Override
public String toString()
{
return this.bn + " - " + this.pg + " - " + this.noOfBills + " - " + this.cost;
}
}
这里是我测试的样本数据的主要方法,你可以收集而不是打印。
public static void main(String[] args)
{
List<Stat> list = new ArrayList<>();
list.add(new Stat(1, "Telephone",1, 1000));
list.add(new Stat(1, "Computer",1, 200));
list.add(new Stat(1, "Telephone",2, 2000));
list.add(new Stat(1, "Computer",1, 200));
list.add(new Stat(2, "Accessories",1, 1500));
list.stream().collect(toMap(Stat::getPg, Function.identity(),
(a, b) -> new Stat(a.getBn(), a.getPg(), a.getNoOfBills() + b.getNoOfBills(), a.getCost() + b.getCost())))
.forEach((id,foo)->System.out.println(foo));
}
- 这里我用的是two argument groupingBy
我有对象 PayBill,其中包含项目列表。对象项目包含文章。本文连接到另一个 Nomenclature 对象,该对象存储为 db table,下一个结构为:
Article ProductGroup
------- ------------
010101 Telephone
040444 Computer
我将我的交易列表解析为具有以下属性的对象 Stat:payBillNumber
、productGroup
、amountOfItems
、sumPriceOfItems
。现在 Stat
的列表如下所示:
BillNumber ProductGroup NumberOfItemsInBill SummaryItemsCostInBill
---------- ------------ ------------------- ----------------------
1 Telephone 1 1000
1 Computer 1 200
1 Telephone 2 2000
1 Computer 1 200
2 Accessories 1 1500
如何使用以下视图将此 List<Stat>
折叠成新的 List<Stat>
:
BillNumber ProductGroup NumberOfItemsInBill SummaryItemsCostInBill
---------- ------------ ------------------- ----------------------
1 Telephone 3 3000
1 Computer 2 400
2 Accessories 1 1500
我希望我的数据按 BillNumber 和 ProductGroup 进行分组,金额与价格相加。是否可以不创建新地图?
我想出了一个涉及中间地图的解决方案,但以实用的方式使用 groupingBy
和 reducing
:
List<Stat> collect = stats.stream()
.collect(Collectors.groupingBy(Stat::getBillNumber, Collectors.groupingBy(Stat::getProductGroup,
Collectors.reducing((left, right) -> new Stat(
left.getBillNumber(),
left.getProductGroup(),
left.getNumberOfItemsInBill() + right.getNumberOfItemsInBill(),
left.getSummaryItemsCostInBill() + right.getSummaryItemsCostInBill()
)))))
.values().stream()
.flatMap(m -> m.values().stream())
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
请注意,这会导致创建大量 Stat
个对象。如果性能是一个问题,你可以想出一些聚合器并使用 Collectors.of
而不是 Collectors.reducing
或者坚持使用 reducing
并在最后映射它。
您必须创建 Stat
构造函数,但它是这样的:
Collection<Stat> aggregated = stats.stream()
.collect(Collectors.toMap(
stat -> new AbstractMap.SimpleEntry<>(stat.getProductGroup(), stat.getBillNumber()),
stat -> stat,
(stat1, stat2) -> new Stat(stat1.getBillNumber(), stat1.getProductGroup(), stat1.getNumberOfItemsInBill() + stat2.getNumberOfItemsInBill(), stat1.getSummaryItemsCostInBill() + stat2.getSummaryItemsCostInBill()
))
.values();
我创建了下面的 class 来表示排序形式的数据。
@Data
class Stat{
int bn;
String pg;
int noOfBills;
int cost;
public Stat(int bn, String pg, int noOfBills, int cost)
{
this.bn = bn;
this.pg = pg;
this.noOfBills = noOfBills;
this.cost = cost;
}
@Override
public String toString()
{
return this.bn + " - " + this.pg + " - " + this.noOfBills + " - " + this.cost;
}
}
这里是我测试的样本数据的主要方法,你可以收集而不是打印。
public static void main(String[] args)
{
List<Stat> list = new ArrayList<>();
list.add(new Stat(1, "Telephone",1, 1000));
list.add(new Stat(1, "Computer",1, 200));
list.add(new Stat(1, "Telephone",2, 2000));
list.add(new Stat(1, "Computer",1, 200));
list.add(new Stat(2, "Accessories",1, 1500));
list.stream().collect(toMap(Stat::getPg, Function.identity(),
(a, b) -> new Stat(a.getBn(), a.getPg(), a.getNoOfBills() + b.getNoOfBills(), a.getCost() + b.getCost())))
.forEach((id,foo)->System.out.println(foo));
}
- 这里我用的是two argument groupingBy