- 项目中有统计的需求,一开始想使用pojo来封装统计的数据但是发现每个统计需求是特有的,也就是说写pojo是可以解决问题的,但是只是用一次统计就不用了,而且后期统计需求增加,pojo会大量增加,所以就思考是否有更好的解决方案
思路:前后端交互用的是json数据,json的结构抽象出来就是一棵树或多棵树,所以生成统计数据实际就是生成一棵树,生成一棵树实际就是对节点的操作(CRUD),所以要统计数据就是要自定义一棵可以过滤的树,当一个统计数据(如:性别)流经这棵树时,首先会根据给定的筛选节点进行流动,遇到分支会进行判断,流向特定的分支,如发现判断后没有给定节点,将会生成新的节点,最后将统计结果放在树的分支的最后一个节点上,从而实现了相对自由的统计结构生成方式
环境:springboot
依赖:net.sf.json.JSONObject(用其它也是可以的,思路一样方法不一样而已)
import net.sf.json.JSONObject;
import java.util.*;
/**
* @author ZC
* @date 2019/12/23
*/
public class Test {
public static void main(String[] args) {
//模拟数据
List<User> userList = new ArrayList<>();
userList.add(new User(1, "1997", "IT部"));
userList.add(new User(0, "1992", "销售部"));
userList.add(new User(0, "1999", "IT部"));
userList.add(new User(1, "1995", "IT部"));
userList.add(new User(0, "1995", "销售部"));
userList.add(new User(1, "1997", "IT部"));
userList.add(new User(0, "1995", "运维部"));
userList.add(new User(1, "1997", "IT部"));
userList.add(new User(0, "1997", "运维部"));
userList.add(new User(1, "1995", "IT部"));
userList.add(new User(0, "1997", "运维部"));
userList.add(new User(1, "1992", "运维部"));
userList.add(new User(1, "1992", "IT部"));
//统计数据的根节点
JSONObject statistics = new JSONObject();
//过滤的一些条件和统计数据
HashMap<String, Object> condittion = new HashMap<>();
//设置根条件(属于哪个公司)
condittion.put("com", "哈皮公司");
//统计
for (User user : userList) {
//设置子条件和统计数据
condittion.put("gender", user.getSex());
condittion.put("dep", user.getDep());
condittion.put("year", user.getBirthday());
//开始统计过滤
jsonDepInc(statistics, condittion);
}
System.out.println(statistics);
}
/**
* @param jsonObject 需要按节点生成统计数据的JSONObject对象
* @param condition 条件
* @return void
* @throws
* @description 部门统计
* @author ZC
* @date 2019/12/24
*/
private static void jsonDepInc(JSONObject jsonObject, Map<String, Object> condition) {
String dep = (String) condition.get("dep");
int gender = (int) condition.get("gender");
String year = (String) condition.get("year");
String com = (String) condition.get("com");
// 以下字符串可以统一定义出去,使用condition传进来,
// 这样便于节点名称的管理,其实就是统计树上的所有节点名称
jsonInc(jsonObject, com, "count");
if (gender == 1) {
jsonInc(jsonObject, com, "gender", "male");
} else {
jsonInc(jsonObject, com, "gender", "female");
}
jsonInc(jsonObject, com, dep, "count");
if (gender == 1) {
jsonInc(jsonObject, com, dep, "gender", "male");
} else {
jsonInc(jsonObject, com, dep, "gender", "female");
}
jsonInc(jsonObject, com, dep, "year", year);
}
/**
* @param jsonObject 需要按节点生成统计数据的JSONObject对象
* @param keys 最终需要加一的节点的访问路径
* @return void
* @throws
* @description 按json节点顺序,对最后一个节点进行自增,建议做为工具方法
* @author ZC
* @date 2019/12/24
*/
private static void jsonInc(JSONObject jsonObject, String... keys) {
LinkedList<String> jsonKeys = new LinkedList<>();
//复制一份String到List中方便节点操作
for (String key : keys) {
jsonKeys.add(key);
}
jsonIncModifyList(jsonObject, jsonKeys);
}
/**
* @param jsonObject 需要按节点生成统计数据的JSONObject对象
* @param keys 最终需要加一的节点的访问路径
* @return void
* @throws
* @description 按json节点顺序,对最后一个节点进行自增,List会改变
* @author ZC
* @date 2019/12/24
*/
private static void jsonIncModifyList(JSONObject jsonObject, LinkedList<String> keys) {
//获取第一个节点
String key = keys.get(0);
if (keys.size() != 1) {//不是最后一个节点时
if (!jsonObject.containsKey(key)) {//当该节点不存在
JSONObject innerJsonObject = new JSONObject();
keys.remove(0);
//进入下一层节点
jsonIncModifyList(innerJsonObject, keys);
//递归回来将新增的有值节点存入
jsonObject.element(key, innerJsonObject);
} else {//当该节点存在
keys.remove(0);
//继续访问下一层节点
jsonIncModifyList(jsonObject.getJSONObject(key), keys);
}
} else {//是最后一个节点时
if (jsonObject.containsKey(key)) {
int incNum = jsonObject.getInt(key);
incNum++;
jsonObject.element(key, incNum);
} else {
jsonObject.element(key, 1);
}
}
}
}
class User {
private Integer gender;
private String birthday;
private String dep;
User() {
}
public User(Integer gender, String birthday, String dep) {
this.gender = gender;
this.birthday = birthday;
this.dep = dep;
}
public Integer getSex() {
return gender;
}
public void setSex(Integer gender) {
this.gender = gender;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getDep() {
return dep;
}
public void setDep(String dep) {
this.dep = dep;
}
}
生成结果:
{
"哈皮公司":{
"count":13,
"gender":{
"male":7,
"female":6
},
"IT部":{
"count":7,
"gender":{
"male":6,
"female":1
},
"year":{
"1992":1,
"1995":2,
"1997":3,
"1999":1
}
},
"销售部":{
"count":2,
"gender":{
"female":2
},
"year":{
"1992":1,
"1995":1
}
},
"运维部":{
"count":4,
"gender":{
"female":3,
"male":1
},
"year":{
"1992":1,
"1995":1,
"1997":2
}
}
}
}
船新版本
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
/**
* @author ZC
* @date 2019/12/23
*/
public class Test {
public static void main(String[] args) {
//根节点
JSONObject jsonObject = new JSONObject();
//Number的子类都可以参与运算
jsonInc(jsonObject, new Short("8"), "哈皮公司", "描述");
jsonInc(jsonObject, new Short("2"), "哈皮公司", "描述");
jsonInc(jsonObject, new Double("0.1"), "哈皮公司", "描述");
jsonInc(jsonObject, new Byte("1"), "哈皮公司", "描述");
jsonInc(jsonObject, new Float("1.2"), "哈皮公司", "描述");
System.out.println(jsonObject);
//实现精确计算
jsonInc(jsonObject, -0.2, "哈皮公司", "描述");
jsonInc(jsonObject, 0.1889, "哈皮公司", "描述");
System.out.println(jsonObject);
//可添加JSONObject可转化的对象
jsonInc(jsonObject, "介是一个公司", "哈皮公司", "描述");
System.out.println(jsonObject);
//实现精确计算
jsonInc(jsonObject, 0.1889, "哈皮公司", "描述");
System.out.println(jsonObject);
jsonInc(jsonObject, -1.1889, "哈皮公司", "描述");
System.out.println(jsonObject);
//可添加JSONObject可转化的对象
jsonInc(jsonObject, new JSONObject().element("内部JSONObject描述", "不可描述"), "哈皮公司", "描述");
System.out.println(jsonObject);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("内部HashMap描述", 5);
jsonInc(jsonObject, hashMap, "哈皮公司", "描述");
System.out.println(jsonObject);
jsonInc(jsonObject, new JSONArray(), "JSONArray", "描述");
System.out.println(jsonObject);
}
/**
* @param jsonObject 需要按节点生成统计数据的JSONObject对象
* @param keys 最终需要追加对象的节点的访问路径
* @param addObj 数字做运算,对象做追加,如果最后一个节点原来已经有对象,将会覆盖
* @description 按json节点顺序,对最后一个节点添加元素或运算
* @author ZC
* @date 2019/12/24
*/
private static void jsonInc(JSONObject jsonObject, Object addObj, String... keys) {
//复制一份String到List中方便节点操作
LinkedList<String> jsonKeys = new LinkedList<>(Arrays.asList(keys));
jsonIncModifyList(jsonObject, addObj, jsonKeys);
}
private static void jsonIncModifyList(JSONObject jsonObject, Object addObj, LinkedList<String> keys) {
//获取第一个节点
String key = keys.get(0);
if (keys.size() != 1) {//不是最后一个节点时
if (!jsonObject.containsKey(key)) {//当该节点不存在
JSONObject innerJsonObject = new JSONObject();
keys.remove(0);
//进入下一层节点
jsonIncModifyList(innerJsonObject, addObj, keys);
//递归回来将新增的有值节点存入
jsonObject.element(key, innerJsonObject);
} else {//当该节点存在
keys.remove(0);
//key对应的不是JSONObject,新建一个覆盖原对象
//此处成立说明对应的访问节点已经存储了非JSONObject对象,如:字符串,集合等
if (!(jsonObject.get(key) instanceof JSONObject)) {
jsonObject.element(key, new JSONObject());
}
//继续访问下一层节点
jsonIncModifyList(jsonObject.getJSONObject(key), addObj, keys);
}
} else {//是最后一个节点时
if (addObj instanceof Number) {//对数字,实现精确计算
//入参的数字
BigDecimal num = new BigDecimal(String.valueOf(addObj));
if (jsonObject.containsKey(key)) {
//将最后一个节点value不是数字类型的初始化为0
if (!(jsonObject.get(key) instanceof Number)) {
jsonObject.element(key, 0);
}
//末节点的数字
BigDecimal sum = new BigDecimal(String.valueOf(jsonObject.get(key)));
sum = sum.add(num);
jsonObject.element(key, sum);
} else {
//初始化数字
jsonObject.element(key, num);
}
} else {//对其它对象,如:字符串,新的jsonObject,集合等
jsonObject.element(key, addObj);
}
}
}
}
生成结果:
{"哈皮公司":{"描述":12.3}}
{"哈皮公司":{"描述":12.2889}}
{"哈皮公司":{"描述":"介是一个公司"}}
{"哈皮公司":{"描述":0.1889}}
{"哈皮公司":{"描述":-1}}
{"哈皮公司":{"描述":{"内部JSONObject描述":"不可描述"}}}
{"哈皮公司":{"描述":{"内部HashMap描述":5}}}
{"哈皮公司":{"描述":{"内部HashMap描述":5}},"JSONArray":{"描述":[]}}
结语:如果大家有新的想法和处理方式请在下方评论,不足之处也请告知,共勉!