一.背景
实际开发中,遇到了一个场景,需要对两个数据源集合进行去重合并,并且对去重合并后的结果集进行数据标签加工汇总。
二.场景演示
现在有两个数据源集合,分别来自不同的端,且两个数据源集合中结构是相同的。
模拟数据源集合1:
Map<String, Object> m0 = new HashMap<>();
m0.put("birthday", "1990年12月27日");
m0.put("graduate", "2014年06月06日");
m0.put("name", "张三");
m0.put("money", "100.66");
Map<String, Object> m1 = new HashMap<>();
m1.put("birthday", "1992年11月11日");
m1.put("graduate", "2016年06月08日");
m1.put("name", "李四");
m1.put("money", "200.88");
List<Map<String,Object>> sourceList1 = new ArrayList<>();
sourceList1.add(m0);
sourceList1.add(m1);
模拟数据源集合2:
Map<String, Object> m2 = new HashMap<>();
m2.put("birthday", "1991年09月01日");
m2.put("graduate", "2015年06月07日");
m2.put("name", "王五");
m2.put("money", "300.99");
Map<String, Object> m3 = new HashMap<>();
m3.put("birthday", "1992年11月11日");
m3.put("graduate", "2015年06月07日");
m3.put("name", "李四");
m3.put("money", "300.99");
List<Map<String, Object>> sourcelist2 = new ArrayList<>();
sourcelist2.add(m2);
sourcelist2.add(m3);
现在需要对两个集合进行去重合并成一个集合,且去重的判定标准是Map中的name+birthday如果都是一样的则视为重复的,且保留sourceList1的结果。
ListUtil.java
package com.hong.util;
import com.alibaba.fastjson.JSONObject;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author wanghong
* @date 2019/04/03 22:27
* 泛型化集合排序工具
**/
public class ListUtil<T> {
/**
* 去重并合并
* 对2个List根据某几个字段进行去重并将结果集合并到一个List中
* l0,l1本身是不重复的,这里注意重复的记录只保留l0中的,所以在实际传参时需要注意l0和l1的顺序
*
* @param l0 数据源集合1
* @param l1 数据源集合2
* @return
* @paran keys
*/
public static List<Map<String, Object>> removeDuplicateAndMerge(List<Map<String, Object>> l0, List<Map<String, Object>> l1, String[] keys) {
l0.addAll(l1);
return l0.stream().collect(
Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> {
StringBuilder sb = new StringBuilder();
for (String key : keys) {
Object val = o.get(key);
if (val instanceof String) {
val = ((String) val).replaceAll("(", "(").replaceAll(")", ")");
}
sb.append(val);
sb.append(";");
}
return sb.deleteCharAt(sb.lastIndexOf(";")).toString();
}))), ArrayList::new));
}
}
String[] keys = {"name","birthday"};
List<Map<String, Object>> list = ListUtil.removeDuplicateAndMerge(sourceList1, sourcelist2, keys);
System.out.println(list);
打印结果:
[{birthday=1990年12月27日, money=100.66, graduate=2014年06月06日, name=张三}, {birthday=1992年11月11日, money=200.88, graduate=2016年06月08日, name=李四}, {birthday=1991年09月01日, money=300.99, graduate=2015年06月07日, name=王五}]
从打印结果中可以看到重复的记录值只保留了sourceList1中的。
三.问题延申
现在需要统计去重合并后结果集中的birthday最小/大值,graduate的最小值,money的总和,如何做?
package com.hong.util;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;
/**
* @author wanghong
* @date 2019/04/26 17:25
* 汇总操作工具类
**/
public class OperateUtil {
/**
* 这里的核心思想是:在每一次遍历集合元素的过程中,同时比较最小/大值,和的累计,而不是将最小/大值的求解分开遍历
*
* @param list 数据集合
* @param t 汇总结果封装对象
* @param summaryList 需要汇总最大/小值/总和的字段信息
* @param <T>
*/
public static <T> void summarizing(List<Map<String, Object>> list, T t, List<Summary> summaryList) {
if (CollectionUtils.isEmpty(list) || t == null || CollectionUtils.isEmpty(summaryList)) {
return;
}
Map<String, Summary> fieldMap = new HashMap<>();
for (Summary sm : summaryList) {
fieldMap.put(sm.getField(), sm);
}
/**
* 取出所有参与汇总的的源数据集字段
*/
Set<String> fields = fieldMap.keySet();
/**
* 汇总结果集Map
* 保存 汇总字段 -> 最小/大值/总和的映射
*/
Map<String, Object> summaryMap = new HashMap<>();
for (Map<String, Object> m : list) {
for (String field : fields) {
Object current = m.get(field);
if (StringUtils.isEmpty(current)) {
continue;
}
Summary summary = fieldMap.get(field);
String maxField = summary.getMaxField();
String minField = summary.getMinField();
String sumField = summary.getSumField();
Comparator comparator = summary.getComparator();
BinaryOperator binaryOperator = summary.getBinaryOperator();
Object maxValue = summaryMap.get(maxField);
Object minValue = summaryMap.get(minField);
Object sumValue = summaryMap.get(sumField);
if (!StringUtils.isEmpty(maxField)) {
if (maxValue == null || comparator.compare(current, maxValue) > 0) {
summaryMap.put(maxField, current);
}
}
if (!StringUtils.isEmpty(minField)) {
if (minValue == null || comparator.compare(current, minValue) < 0) {
summaryMap.put(minField, current);
}
}
if (!StringUtils.isEmpty(sumField)) {
if (sumValue == null) {
summaryMap.put(sumField, current);
} else {
summaryMap.put(sumField, binaryOperator.apply(current, sumValue));
}
}
}
}
Class<?> clazz = t.getClass();
for (Map.Entry<String, Object> entry : summaryMap.entrySet()) {
String key = entry.getKey();
try {
Field field = clazz.getDeclaredField(key);
field.setAccessible(true);
field.set(t, entry.getValue());
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static class SummaryInfo {
private String minBirthday;
private String maxBirthday;
private String name;
private String minGraduate;
private String sumMoney;
public String getMinBirthday() {
return minBirthday;
}
public void setMinBirthday(String minBirthday) {
this.minBirthday = minBirthday;
}
public String getMaxBirthday() {
return maxBirthday;
}
public void setMaxBirthday(String maxBirthday) {
this.maxBirthday = maxBirthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMinGraduate() {
return minGraduate;
}
public void setMinGraduate(String minGraduate) {
this.minGraduate = minGraduate;
}
public String getSumMoney() {
return sumMoney;
}
public void setSumMoney(String sumMoney) {
this.sumMoney = sumMoney;
}
@Override
public String toString() {
return "SummaryInfo{" +
"minBirthday='" + minBirthday + '\'' +
", maxBirthday='" + maxBirthday + '\'' +
", name='" + name + '\'' +
", minGraduate='" + minGraduate + '\'' +
", sumMoney='" + sumMoney + '\'' +
'}';
}
}
/**
* 汇总操作
*/
public static class Summary {
/**
* 源数据集中需要汇总的字段
*/
private String field;
/**
* 汇总后对应的最小值接收字段
*/
private String minField;
/**
* 汇总后对应的最大值接收字段
*/
private String maxField;
/**
* 自定义比较器
*/
private Comparator comparator;
/**
* 汇总对应的总和接收字段
*/
private String sumField;
/**
* 求和操作
*/
private BinaryOperator binaryOperator;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getMinField() {
return minField;
}
public void setMinField(String minField) {
this.minField = minField;
}
public String getMaxField() {
return maxField;
}
public void setMaxField(String maxField) {
this.maxField = maxField;
}
public Comparator getComparator() {
return comparator;
}
public void setComparator(Comparator comparator) {
this.comparator = comparator;
}
public String getSumField() {
return sumField;
}
public void setSumField(String sumField) {
this.sumField = sumField;
}
public BinaryOperator getBinaryOperator() {
return binaryOperator;
}
public void setBinaryOperator(BinaryOperator binaryOperator) {
this.binaryOperator = binaryOperator;
}
}
SummaryInfo summaryInfo = new SummaryInfo();
summaryInfo.setName("wanghong");
Summary sc0 = new Summary();
sc0.setField("birthday");
sc0.setMinField("minBirthday");
sc0.setMaxField("maxBirthday");
sc0.setComparator(Comparator.comparing(DateUtil::parseDate));
Summary sc1 = new Summary();
sc1.setField("graduate");
sc1.setMinField("minGraduate");
sc1.setComparator(Comparator.comparing(DateUtil::parseDate));
Summary sc2 = new Summary();
sc2.setField("money");
sc2.setSumField("sumMoney");
sc2.setBinaryOperator((a, b) -> {
BigDecimal b0 = new BigDecimal(a.toString());
BigDecimal b1 = new BigDecimal(b.toString());
return b0.add(b1).toString();
});
List<Summary> summaryList = new ArrayList<>();
summaryList.add(sc0);
summaryList.add(sc1);
summaryList.add(sc2);
summarizing(list, summaryInfo, summaryList);
System.out.println(summaryInfo);
打印结果:
SummaryInfo{minBirthday='1990年12月27日', maxBirthday='1992年11月11日', name='wanghong', minGraduate='2014年06月06日', sumMoney='602.53'}