在工作中会遇到一些List 需要按照 多个字段分组,其实可以这样写。
实体类
//这里的注解是省的写get set 等方法 不过多解释
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
//订单区域
private String address;
//订单类型
private String type;
//收款金额
private int price;
//退款金额
private int refund;
}
Demo
public class Test {
public static void main(String[] args) {
//这里模拟数据
List<Order> orders = Arrays.asList( new Order("安徽","内部订单",100,0),
new Order("安徽","内部订单",200,50),
new Order("安徽","外部订单",100,100),
new Order("河南","内部订单",100,0),
new Order("河南","外部订单",300,0),
new Order("河南","内部订单",200,100),
new Order("河北","内部订单",100,0),
new Order("河北","内部订单",100,0),
new Order("河北","内部订单",100,0),
new Order("江西","内部订单",100,50),
new Order("江西","外部订单",100,100));
//先输出一下原始数据
System.out.println("分组之前的数据");
orders.forEach(System.out :: println);
System.out.println("分组之后的数据");
//准备好数据之后,调用一下方法
List<Order> ordersByGroup = groupByAddressAndType(orders);
//输出一下分组之后的数据
ordersByGroup.forEach(System.out :: println);
}
/**
* 需求:
* 按照地址,和订单类型 分组数据 并且 收款金额,和退款金额都要统计
*/
public static List<Order> groupByAddressAndType(List<Order> orders) {
//这个map 是用来统计收款金额的
Map<String, IntSummaryStatistics> priceMap = orders.parallelStream()
.collect(Collectors.groupingBy(
//这里按照getAddress 地址 getType 类型来作为分组的条件 最后想要统计的是交易金额
a -> a.getAddress() + "_" + a.getType(), Collectors.summarizingInt(a -> a.getPrice()))
);
//这个map 是用来统计退款金额的
Map<String, IntSummaryStatistics> refundMap = orders.parallelStream()
.collect(Collectors.groupingBy(
//这里按照getAddress 地址 getType 类型来作为分组的条件 最后想要统计的是交易金额
a -> a.getAddress() + "_" + a.getType(), Collectors.summarizingInt(a -> a.getRefund()))
);
orders.forEach(order -> {
//这个获取map对应值
long price = priceMap.get(order.getAddress() + "_" + order.getType()).getSum();
long refund = refundMap.get(order.getAddress() + "_" + order.getType()).getSum();
//这里把取出来的值 赋给order
order.setPrice((int) price);
order.setRefund((int) refund);
});
//最后返回
return orders.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toCollection(
() -> new TreeSet<>(
// 这里的规则和 上面分组的规则一样
Comparator.comparing(u -> u.getAddress() + "_" + u.getType())
)), ArrayList::new));
}
}
输出的结果如下
分组之前的数据
Order(address=安徽, type=内部订单, price=100, refund=0)
Order(address=安徽, type=内部订单, price=200, refund=50)
Order(address=安徽, type=外部订单, price=100, refund=100)
Order(address=河南, type=内部订单, price=100, refund=0)
Order(address=河南, type=外部订单, price=300, refund=0)
Order(address=河南, type=内部订单, price=200, refund=100)
Order(address=河北, type=内部订单, price=100, refund=0)
Order(address=河北, type=内部订单, price=100, refund=0)
Order(address=河北, type=内部订单, price=100, refund=0)
Order(address=江西, type=内部订单, price=100, refund=50)
Order(address=江西, type=外部订单, price=100, refund=100)
分组之后的数据
Order(address=安徽, type=内部订单, price=300, refund=50)
Order(address=安徽, type=外部订单, price=100, refund=100)
Order(address=江西, type=内部订单, price=100, refund=50)
Order(address=江西, type=外部订单, price=100, refund=100)
Order(address=河北, type=内部订单, price=300, refund=0)
Order(address=河南, type=内部订单, price=300, refund=100)
Order(address=河南, type=外部订单, price=300, refund=0)
分析数据,以安徽为例,
2个内部订单 收款金额price 分别为 100,200 分组合并后 应该为 300 退款金额 分别为 0,50 合并后为50
1个外部订单 收款金额price 分别为 100 分组合并后 应该为 100 退款金额 分别为 100 合并后为100
以此类推,数据都是正确的,不再逐个分析
此为List<对象> 的多分组
List<Map<String, Object>> 类型的多字段分组 也是如此类推,
如果2个字段分组还不能满足你的需求,
a -> a.getAddress() + "_" + a.getType() + "_" + a.getXXX() //后面多拼 几个字段
Map<String, IntSummaryStatistics> map //如果 要统计的数据更多,就多几个这个map