You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Java Expression Library(JEL)实现二元表达式快速求值

结论

JEL完全匹配你的使用需求。它和mxParser这类解释执行的表达式库不同,会直接把输入的字符串表达式编译成JVM原生字节码,批量求值的性能和你直接硬编码调用Java Math库的差距极小,运行效率远高于mxParser,非常适合需要传入多组参数反复求值的场景。

具体实现流程

操作核心是把「表达式编译」和「参数传入求值」两个步骤完全拆开,只需要编译一次表达式,后续求值不需要重复做语法解析,这也是JEL性能高的核心原因:

  • 第一步:定义变量存储容器,用来存放每次求值传入的x、y值,JEL编译时会直接绑定变量的访问路径,求值时不需要做参数名的哈希查找。
  • 第二步:初始化JEL的函数库,直接导入java.lang.Math下的所有公开静态方法,就可以直接支持sin、cos、sqrt、pow等所有常用数学函数,不需要自己逐个实现。
  • 第三步:处理表达式语法兼容:JEL默认的^运算符是按位异或,不是幂运算,你可以在读取用户输入的表达式后,简单把幂运算写法替换为pow(底数,指数)的形式,就能兼容你示例里的sin(x^2+y^2)这类写法。
  • 第四步:传入处理好的表达式字符串,指定返回值为double类型,编译得到可重复执行的表达式对象。
  • 第五步:批量求值时,只需要给变量容器里的x、y赋对应的值,调用表达式对象的求值方法即可,不需要重复解析表达式。
可直接运行的参考代码
import gnu.jel.CompiledExpression;
import gnu.jel.Library;
import gnu.jel.DVMap;

// 变量存储容器,JEL编译时会直接绑定这个类里的变量
public class VarContext extends DVMap {
    public double x;
    public double y;

    @Override
    public String getTypeName(String varName) {
        if ("x".equals(varName) || "y".equals(varName)) {
            return "double";
        }
        return null;
    }
}

public class JELEvalTest {
    public static void main(String[] args) throws Exception {
        VarContext ctx = new VarContext();
        // 导入Math类所有静态方法作为可用函数
        Class<?>[] staticLibs = {Math.class};
        Library lib = new Library(staticLibs, new Class[]{}, null, ctx, null);

        // 原始表达式是sin(x^2+y^2),替换为JEL支持的pow写法
        String expr = "sin(pow(x,2) + pow(y,2))";
        // 仅需编译一次,后续可反复求值
        CompiledExpression compiledExpr = gnu.jel.Compiler.compile(expr, lib, double.class);

        // 批量传入不同参数求值
        double[][] paramGroups = {{0, 0}, {1, 1}, {Math.PI/4, Math.PI/4}};
        for (double[] params : paramGroups) {
            ctx.x = params[0];
            ctx.y = params[1];
            double res = (double) compiledExpr.evaluate(ctx);
            System.out.printf("x=%.2f, y=%.2f, 计算结果=%.4f%n", ctx.x, ctx.y, res);
        }
    }
}
性能优化提示
  • 不要把编译逻辑放到求值循环里,只要表达式不变,CompiledExpression对象可以一直复用,这是保证性能的核心。
  • 直接导入Math类的原生方法比自己包装一层自定义函数性能高30%以上,没有特殊需求不要额外封装数学函数。
  • 如果需要支持更多自定义函数,只需要把包含对应静态方法的类加到staticLibs数组里即可,JEL会自动识别方法签名,不需要额外注册。

内容的提问来源于stack exchange,提问作者Rango

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 00:39:36