使用Java8新特性:在对象集合里, 对一个属性(集合 )求平均值
需求场景:客服系统的大屏监控页面,展示客服接待量的时分图和折线图,而查询展示的方式有两种:
- 单天查询:以小时为单位展示接待量;
- 多天查询:以天为单位展示接待量,没有精确到每天的各个小时;
- 新需求是 要在多天查询的基础上进行升级,要求展示 多天且以小时为单位的客服接待量,而数据是选中时间范围内的接待平均值:如下图(对应上图时间范围数据)
代码如下
需求了解以后,这个问题的就出现了,通过一些逻辑代码,我拿到了以小时为单位的多天的数据,但是却是全量的数据,即选中时间范围内,每个客服对应的每天0-23点的数据,3天就==3*24个小时数据;
那么问题来了:如何 去在对象集合里面 对 对象进行去重,且合并对象里的接待量(Map<String ,String> item)属性,并求其平均值?
- 对象信息(简化):
private String staffName; //客服名称
private String groupName; //技能组名称
private String operationStatus;//查询的属性 1 排队会话数 2 未排队会话数 3 咨询会话数
private String status;//1-按天,0-按小时
private Map<String ,String> item = new LinkedHashMap<String ,String>();//key-小时或日期,value-相应日期对应的值
- 需要处理的数据格式:
注意:item属性 中:“2021-11-15-0”:“0” (2021-11-15是日期,第一个0是0点,第二个 0 是接待量)
[
{
"staffName":"一级客服A",
"groupName":"泡泡泡泡",
"status":"0",
"item":{
"2021-11-15-0":"0",
"2021-11-15-1":"0",
"2021-11-15-2":"0",
"2021-11-15-3":"0",
"2021-11-15-4":"0",
"2021-11-15-5":"0",
"2021-11-15-6":"0",
"2021-11-15-7":"0",
"2021-11-15-8":"0",
"2021-11-15-9":"0",
"2021-11-15-10":"1",
"2021-11-15-11":"1",
"2021-11-15-12":"0",
"2021-11-15-13":"0",
"2021-11-15-14":"1",
"2021-11-15-15":"1",
"2021-11-15-16":"0",
"2021-11-15-17":"0",
"2021-11-15-18":"0",
"2021-11-15-19":"0",
"2021-11-15-20":"0",
"2021-11-15-21":"0",
"2021-11-15-22":"0",
"2021-11-15-23":"0"
}
},
{
"staffName":"",
"groupName":"泡泡泡泡",
"status":"0",
"item":Object{...}
},
{
"staffName":"加急客服",
"groupName":"其他",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"其他",
"status":"0",
"item":Object{...}
},
{
"groupName":"总计",
"status":"0",
"item":Object{...}
},
{
"staffName":"一级客服A",
"groupName":"泡泡泡泡",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"泡泡泡泡",
"status":"0",
"item":Object{...}
},
{
"staffName":"加急客服",
"groupName":"其他",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"其他",
"status":"0",
"item":Object{...}
},
{
"groupName":"总计",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"测试",
"status":"0",
"item":Object{...}
},
{
"staffName":"豆子",
"groupName":"会员中心",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"会员中心",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"泡泡抽盒机",
"status":"0",
"item":Object{...}
},
{
"groupName":"总计",
"status":"0",
"item":Object{...}
}
]
- 处理代码:
/**
1.customerDatas 需要处理数据集合(客服数据、技能组数据),startDate 和 endDate 是选中的时间范围 如:2021-11-17 - 2021-11-19 3天;
2.因测试环境数据问题,并不是每个客服每天都会有接待数据;
3.因为数据分以每个客服为单位 以及 客服所在技能组为单位,所以会对数据进行区分处理;
3.求得的平均值是以四舍五入并保留两位小数为标准的BigDecimal;
**/
private List<customerData> pressureAverageMethod(List<customerData> customerDatas, String startDate, String endDate) {
List<customerData> list = new ArrayList<customerData>();
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date start = sdf.parse(startDate);
Date end = sdf.parse(endDate);
List<String> listDate = TimeUtil.getBetweenDates(start, end);
//用于平均分,==3
int num = listDate.size();
//有数据的技能组名 集合
HashMap<String, String> objectObjectHashMap = new HashMap<>( );
for (customerData customerData : customerDatas){
//获取同一个人的数据
if (StringUtil.isNotNull(customerData.getGroupName())){
objectObjectHashMap.put(customerData.getGroupName(),customerData.getGroupName());
}
}
// logger.info(new Gson().toJson(objectObjectHashMap));
//当前数据 有客服名和技能组名
ArrayList<customerData> stringArrayAll = new ArrayList<customerData>( );
//当前数据 只有技能组名(以技能组分组后数据)
ArrayList<customerData> stringArrayOnlyOne= new ArrayList<customerData>( );
for (customerData customerData : customerDatas){
if (StringUtil.isNotNull(customerData.getStaffName()) &&
objectObjectHashMap.containsKey(customerData.getGroupName())){
//有客服名和技能组名
stringArrayAll.add(customerData);
}
if ((StringUtil.isNull(customerData.getStaffName()) && objectObjectHashMap.containsKey(customerData.getGroupName()))
|| "总计".equals(customerData.getGroupName())){
//只有技能组名
stringArrayOnlyOne.add(customerData);
}
}
//有客服名和技能组名 逻辑处理开始
Map<String,List<customerData>> userMapAll = new HashMap<>();
stringArrayAll.stream().collect(Collectors.groupingBy(customerData::getStaffName,Collectors.toList()))
.forEach(userMapAll::put);
userMapAll.forEach((staffName,beanList)->{
customerData allBean = new customerData( );
allBean.setStaffName(staffName);
allBean.setGroupName(beanList.get(0).getGroupName());
allBean.setStatus("0");
Map<String, String> item = new LinkedHashMap<>();
for (customerData bean : beanList){
item.putAll(bean.getItem());
}
Map<String, BigDecimal> item2 = new LinkedHashMap<String, BigDecimal>();
for (int i=0; i<24;i++){
item2.put(""+i, BigDecimal.valueOf(0.0));
}
// logger.info("pressureAverageMethod item2.size="+item2.size());
item.forEach((name,value)->{
//Integer.parseInt(name.substring(name.lastIndexOf("-")+1))
String substring = name.substring(name.lastIndexOf("-") + 1);
if (item2.containsKey(substring)){
item2.put(substring, item2.get(substring).add(new BigDecimal(value)));
}
});
item2.forEach((key,value)->{
item2.put(key, item2.get(key).divide(BigDecimal.valueOf(num),2, RoundingMode.HALF_UP));
});
//最后返回 平均数据的 (item)map
Map<String, String> itemEnd = new LinkedHashMap<>();
item2.forEach((key,value)->{
itemEnd.put(key, String.valueOf(value));
});
allBean.setItem(itemEnd);
list.add(allBean);
});
//有客服名和技能组名 逻辑处理结束
//只有技能组名 逻辑处理开始
Map<String,List<customerData>> userMapOnlyOne = new HashMap<>();
stringArrayOnlyOne.stream().collect(Collectors.groupingBy(customerData::getGroupName,Collectors.toList()))
.forEach(userMapOnlyOne::put);
userMapOnlyOne.forEach((groupName,beanList)->{
customerData onlyOneBean = new customerData( );
onlyOneBean.setGroupName(groupName);
onlyOneBean.setStaffName("");
onlyOneBean.setStatus("0");
Map<String, String> item = new LinkedHashMap<>();
// logger.info("csMethod bean="+new Gson().toJson(beanList));
for (customerData bean : beanList){
item.putAll(bean.getItem());
}
Map<String, BigDecimal> item2 = new LinkedHashMap<String, BigDecimal>();
for (int i=0; i<24;i++){
item2.put(""+i, BigDecimal.valueOf(0.0));
}
item.forEach((name,value)->{
String substring = name.substring(name.lastIndexOf("-") + 1);
if (item2.containsKey(substring)){
item2.put(substring, item2.get(substring).add(new BigDecimal(value)));
}
});
item2.forEach((key,value)->{
item2.put(key, item2.get(key).divide(BigDecimal.valueOf(num),2, RoundingMode.HALF_UP));
});
//最后返回 平均数据的 (item)map
Map<String, String> itemEnd = new LinkedHashMap<>();
item2.forEach((key,value)->{
itemEnd.put(key, String.valueOf(value));
});
onlyOneBean.setItem(itemEnd);
list.add(onlyOneBean);
});
//只有技能组名 逻辑处理开始
} catch (ParseException e) {
e.printStackTrace( );
}
return list;
}
- 最后处理完的数据为:
[
{
"staffName":"一级客服A",
"groupName":"���泡泡泡",
"status":"0",
"item":{
"0":"0.00",
"1":"0.00",
"2":"0.00",
"3":"0.00",
"4":"0.00",
"5":"0.00",
"6":"0.00",
"7":"0.00",
"8":"0.00",
"9":"0.00",
"10":"0.29",
"11":"0.29",
"12":"0.00",
"13":"0.00",
"14":"0.00",
"15":"0.14",
"16":"0.00",
"17":"0.14",
"18":"0.00",
"19":"0.00",
"20":"0.00",
"21":"0.00",
"22":"0.00",
"23":"0.00"
}
},
{
"staffName":"加急客服",
"groupName":"其他",
"status":"0",
"item":Object{...}
},
{
"staffName":"豆子",
"groupName":"会员中心",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"泡泡泡泡",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"其他",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"会员中心",
"status":"0",
"item":Object{...}
},
{
"staffName":"",
"groupName":"总计",
"status":"0",
"item":Object{...}
}
]
最后,其实这种数据处理场景 需要用到最核心的代码是 java8的 新特性:stream()方法和collect()方法,有兴趣的朋友可以去找一下专业博客,这些方法用来处理 集合数据很方便实用