栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

自定义公式计算动态指标值(数据对象结合属性名公式计算公式结果)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

自定义公式计算动态指标值(数据对象结合属性名公式计算公式结果)

前言

由于近期开发工作涉及到动态指标的计算输出,把度娘都问烦了还是没有找到合适的解决方案(强迫症,为了这一个功能而引入第三方依赖不值得 [捂脸] )。

先上运算符对应的ASCLL值
    
方案一、尝试逐字节解析,遇到运算符或结尾,将已缓存的两个参数及运算符号计算结果输出
import java.util.*;
import java.util.regex.Matcher;

    public float deal(String formula, Class indicator, Object data) throws NoSuchFieldException, IllegalAccessException {
        formula = formula.replaceAll(" ", "");
        char[] chars = formula.toCharArray();
        String patten = "\(\)*+-/\[]";
        Matcher matcher = Pattern.compile(patten).matcher(formula);
        float result = 0;
        StringBuilder temp = new StringBuilder();
        char lastSign = 20;
        float lastParam = 0F;
        float curParam = 0F;
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            if (c != 20) {
                if (signs.contains(c)) {
                    curParam = appendFinish(temp, indicator);
                    result = innerDeal(lastParam, curParam, lastSign);
                    lastSign = c;
                } else {
                    temp.append(c);
                }
            }
        }
        return result;
    }

    private float appendFinish(StringBuilder temp, Class indicator) throws NoSuchFieldException, IllegalAccessException {
        if (temp.toString().equals("100%")) return 1.0f;
        return indicator.getDeclaredField(temp.toString()).getFloat(null);
    }

    public float innerDeal(float lastParam, float curValue, char operator)
            throws NoSuchFieldException, IllegalAccessException {
        switch (operator) {
            case 20: {

            }
            
            case 91:
                
            case 40:
                
            case 42:
                return lastParam * curValue;
            
            case 43:
                return lastParam + curValue;
            
            case 45:
                return lastParam - curValue;
            
            case 47:
                return lastParam / curValue;
            
            case 41:
                
            case 93:
                return lastParam;
        }
        return 0;
    }
结果发现这个方案只能处理运算符优先级相同的公式,如果存在括号或者加减与乘除取模同时出现的时候需要递归或者嵌套业务处理,这肯定不行啊,优先级低的再前面,那得缓存多少临时结果才合适?而且如果公式里面多放几个括号那得加多少业务代码?很明显这不符合强迫症患者的风格,方案一告逝 几经曲折,有那么一瞬间脑洞大开,甚至天灵盖都被掀开的那种,然后出现了方案二 方案二 1、还是对公式的字节遍历,非运算符的字符直接拼接临时串(StringBuilder),遇到运算符则先将临时串(完整的属性名)利用反射获取源数据中的属性值,并将属性值拼接到最终串(StringBuilder),并重置临时串,最终得到纯数值的计算公式。 公式计算,根据运算符的优先级,首先用括号切分,然后对各部分的乘除取模运算优先计算,接着是加减,最后就是括号切分的各部分再次计算。 好家伙,还是回到方案一的坑里了?难道最终还是要用第三方依赖吗?那指定是不服啊,咋整?莽吧,终于“JavaScript”这个词给我蹦了出来,想到了JS是可以处理这种东西的,可是却忘记rt包里本身就是有可以执行JS脚本的引擎,结了,给我整!! 方案三、使用JDK的rt包的JS引擎对纯数值公式计算
package com.dr.service;


import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;


public class FormulaUtils {
    
    static ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
    
     
    static Set signs = new HashSet() {{
        this.addAll(Arrays.asList(new Character[]{20, 37, 40, 41, 42, 43, 45, 47, 91, 93}));
    }};
	
    static Class temp;

    
    public static double calculateFormula(String formula, Object param) throws Exception {
        if (!param.getClass().equals(temp)) temp = param.getClass();
        
        formula = formula.replaceAll(" ", "") + (char) 20;
        
        char[] chars = formula.toCharArray();
        StringBuilder resStr = new StringBuilder();
        if (chars.length > 0) {
            StringBuilder tempStr = new StringBuilder();
            for (char cur : chars) {
                
                if (signs.contains(cur)) {
                    if (tempStr.length() > 0) {
                        String column = tempStr.toString();
                        
                        if (column.equals("100%")) resStr.append("1");
                            
                        else if (isNumeric(column)) resStr.append(column);
                        else {
                            
                            Field field = temp.getDeclaredField(column);
                            field.setAccessible(true);
                            Object value = field.get(param);
                            resStr.append(value);
                            
                            tempStr = new StringBuilder();
                        }
                    }
                    
                    if (resStr.length() > 0 && cur != 20) resStr.append(cur);
                    
                } else tempStr.append(cur);
            }
        }
        
        return (double) jse.eval(resStr.toString());
    }


    
    public static boolean isNumeric(String str) {
        return Pattern.compile("[-+]?(\d+|\d+\.+\d+)$").matcher(str).matches();
    }
}
测试方法
    public static void main(String[] args) throws Exception {
        ArrayList strings = new ArrayList() {{
            add("a+b-c/d+(f+g)+e");
            add("a-b+c/d+(f+g)+e");
            add("a*b-c/d+(f+g)+e");
            add("a/b+c/d+(f+g)+e");
            add("a%b+c/d+(f+g)+e");
            add("a*b+c/d+(f+g)+e");
        }};

        for (String formula : strings) {
            int a = 0;
            long millis = System.currentTimeMillis();
            System.out.println(millis);
            do {
                FormulaUtils.calculateFormula(formula, new CalculateParam());
                a++;
            } while (a < 200000);
            System.out.println(System.currentTimeMillis() - millis + "====" + a);
        }
        System.out.println(1.1 + 2.2 - 3.3 / 4.4 + (6.6 + 7.7) + 5.5);
结果如下
1659669622577
3540====200000
1659669626117
2148====200000
1659669628266
2174====200000
1659669630441
2269====200000
1659669632710
2384====200000
1659669635094
2192====200000
22.35
至此,完事,经测试发现第一次会因为获取反射对象耗时偏高,后续二十万次计算耗时都稳定在2200-2500ms上下,考虑通用,把反射对象缓存,后续考虑使用反射池优化。

感谢浏览,觉得有用的话还望点赞收藏

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1035289.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号