阅读更多

    《飞行大亨》是我很喜欢的一部电影,不过这里我想介绍的是一个叫Aviator的开源的Java表达式求值器。


一、轮子的必要性


    表达式的求值上,java的选择非常多,强大的如Groovy、JRuby,N年没维护的beanshell,包括javaeye上朋友的IKExpression。为什么还需要Aviator?或者说Aviator的特点是什么?

    我将Aviator定位在Groovy这样全功能的脚本和IKExpression这样的简易的表达式求值之间的东西,如果你不希望带上Groovy那么庞大的jar却只用上一点点的功能,如果你希望功能和性能上比IKExpression好那么一些,那么也许你可以考虑Aviator。

    Aviator的设计思路跟利用GroovyObject的求值是一样,通过编译并动态生成字节码的方式将表达式编译成一个类,然后反射执行这个类,因此会在效率上比纯解释执行的IKExpression好一些。

 

aviator结构图

二、让轮子转起来。

 

求算术表达式

 

import com.googlecode.aviator.AviatorEvaluator;


public class SimpleExample {
    public static void main(String[] args) {
        Long result = (Long) AviatorEvaluator.execute("1+2+3");
        System.out.println(result);
    }
}

 

执行入口统一为AviatorEvaluator类,它有一系列静态方法。

 

逻辑表达式和关系运算

AviatorEvaluator.execute("3>1 && 2!=4 || true");

 

Aviator支持所有的关系运算符和算术运算符,不支持位运算,同时支持表达式的优先级,优先级跟Java的运算符一样,并且支持通过括号来强制优先级。

 

使用变量和字符串相加

       String yourname = “aviator”;
        Map<String, Object> env = new HashMap<String, Object>();
        env.put("yourname", yourname);
        String result = (String) AviatorEvaluator.execute(" 'hello ' + yourname ", env);
        System.out.println(result);

 打印:

hello aviator

 

字符串可以单引号也可以双引号括起来,并且支持转义字符。变量名称只要是合法的java identifer即可,变量需要用户传入,通过Map<String,Object>指定变量名和值是什么,这里的变量是yourname。

 

变量的访问支持嵌套访问,也就是dot操作符来访问变量里的属性,假设我们有一个Foo类:

 

 public static class Foo {
        int i;
        float f;
        Date date = new Date();

        public Foo(int i, float f, Date date) {
            super();
            this.i = i;
            this.f = f;
            this.date = date;
        }

        public int getI() {
            return i;
        }

        public void setI(int i) {
            this.i = i;
        }

        public float getF() {
            return f;
        }

        public void setF(float f) {
            this.f = f;
        }

        public Date getDate() {
            return date;
        }

        public void setDate(Date date) {
            this.date = date;
        }

    }

 

然后在使用一个表达式来描述Foo里的各种属性:

 

        Foo foo = new Foo(100, 3.14f, new Date());
        Map<String, Object> env = new HashMap<String, Object>();
        env.put("foo", foo);

        String result =
                (String) AviatorEvaluator.execute(
                    " '[foo i='+ foo.i + ' f='+foo.f+' year='+(foo.date.year+1900)+ ' month='+foo.date.month +']' ",
                    env);

 我们可以通过foo.date.year的方式来访问变量foo中date属性的year值,这是利用commons-beanutils的反射功能实现的,前提是你的变量是合法的JavaBean(public、getter缺一不可)。

 

三元表达式

AviatorEvaluator.execute("3>0? 'yes':'no'");

 

 

上面都还是一个求值器表达式的常见功能,下面要描述的是Aviator的一些偏脚本性的功能。

 

类Ruby、Perl的正则匹配,匹配email地址:

AviatorEvaluator.execute("'killme2008'=~/([\\w0-8]+@\\w+[\\.\\w+]+)/ ");

 成功的话返回true,否则返回false。//括起来的字符序列成为正则表达式,=~操作符用于匹配。匹配只能在String和Pattern之间。


匹配成功,获得匹配的分组,利用变量$digit

 

AviatorEvaluator.execute("'killme2008@gmail.com'=~/([\\w0-8]+@\\w+[\\.\\w+]+)/ ? $1:'unknow'");

 匹配成功返回$1,表示第一个匹配的分组,也就是用户名 killme2008

 

函数调用

AviatorEvaluator.execute("sysdate()");

 sysdate()是一个内置函数,返回当前日期,跟new java.util.Date()效果相同。

 

更多内置函数:

AviatorEvaluator.execute("string.length('hello')");    // 求字符串长度
AviatorEvaluator.execute("string.contains('hello','h')");  //判断字符串是否包含字符串
AviatorEvaluator.execute("string.startsWith('hello','h')");  //是否以子串开头
AviatorEvaluator.execute("string.endsWith('hello','llo')");  是否以子串结尾

AviatorEvaluator.execute("math.pow(-3,2)");   // 求n次方
AviatorEvaluator.execute("math.sqrt(14.0)");   //开平方根
AviatorEvaluator.execute("math.sin(20)");    //正弦函数

 

可以看到Aviator的函数调用风格非常类似lua或者c。

 

自定义函数,实现AviatorFunction接口并注册即可,比如我们实现一个add函数用于相加:

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorDouble;
import com.googlecode.aviator.runtime.type.AviatorFunction;
import com.googlecode.aviator.runtime.type.AviatorObject;
class AddFunction implements AviatorFunction {

        public AviatorObject call(Map<String, Object> env, AviatorObject... args) {
            if (args.length != 2) {
                throw new IllegalArgumentException("Add only supports two arguments");
            }
            Number left = FunctionUtils.getNumberValue(0, args, env);
            Number right = FunctionUtils.getNumberValue(1, args, env);
            return new AviatorDouble(left.doubleValue() + right.doubleValue());
        }


        public String getName() {
            return "add";
        }

    }

 

注册并调用:

        AviatorEvaluator.addFunction(new AddFunction());
        System.out.println(AviatorEvaluator.execute("add(1,2)"));
        System.out.println(AviatorEvaluator.execute("add(add(1,2),100)"));

 

函数可以嵌套调用。

 

三、不公平的性能测试

 

  基本介绍完了,最后给些测试的数据,下列的测试场景都是每个表达式预先编译,然后执行1000万次,测量执行耗时。

 

场景1:

算术表达式

1000+100.0*99-(600-3*15)/(((68-9)-3)*2-100)+10000%7*71

结果:

测试 耗时(单位:秒)
Aviator 14.0
Groovy 79.6
IKExpression 159.2

 

场景2:

计算逻辑表达式和三元表达式混合:

6.7-100>39.6 ? 5==5? 4+5:6-1 : !(100%3-39.0<27) ? 8*2-199: 100%3

测试结果:

测试 耗时(单位:秒)
Aviator 11.0
Groovy 13.0
IKExpression 168.8

 

场景3:

计算算术表达式和逻辑表达式的混合,带有5个变量的表达式:

i * pi + (d * b - 199) / (1 - d * pi) - (2 + 100 - i / pi) % 99 ==i * pi + (d * b - 199) / (1 - d * pi) - (2 + 100 - i / pi) % 99

变量设定为:

        int i = 100;
        float pi = 3.14f;
        double d = -3.9;
        byte b = (byte) 4;
        boolean bool=false;

每次执行前都重新设置这些变量的值。

结果:

测试 耗时(单位:秒)
Aviator 31.2
Groovy 9.7
IKExpression 编译错误

 

场景4:

  • Aviator执行 sysdate()
  • groovy执行 new java.util.Date()
  • IKExpression执行 $SYSDATE()

结果:

测试 耗时(单位:秒)
Aviator 22.6
Groovy 13.9
IKExpression 25.4

 

 

原始的测试报告在这里

 

四、结语

 

能看到这里,并且感兴趣的朋友请点击项目主页:

http://code.google.com/p/aviator/

 

下载地址:

http://code.google.com/p/aviator/downloads/list

 

完整的用户手册:

 

http://code.google.com/p/aviator/wiki/User_Guide_zh

目前版本仍然是1.0.0-RC,希望更多朋友试用并最终release。有什么疑问或者建议请跟贴。

18
0
评论 共 13 条 请登录后发表评论
13 楼 w846492130_1 2016-09-07 17:58
确实是不公平测试
简单来说:为什么要使用动态表达式,原因是因为很多计算时,表达式是动态的,每次计算时都不一样,所以你的100W测试只能凸显表达式缓存后的计算结果,没有体现出100W次不同的表达式计算时的运行效率,以及内存消耗情况。
12 楼 superhanliu 2010-08-25 16:43
只支持数学运算啊,有没有支持动态字符串比较的啊?
11 楼 jindw 2010-07-05 11:06
呵呵,年老眼花,罪过罪过。
那确实是你要快一点点:)


dennis_zane 写道
jindw 写道
jindw 写道
貌似性能改经空间依然很大很大呢。
我在我普通笔记本上试了一下Lite 模板表达式计算你的第一个实例:000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71
消耗时间只有2031104791纳秒(2秒多点)

@Test
public void testEL() {
	Expression el = ExpressionFactoryImpl.getInstance().create(
				"1000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71");
	int c = 100*10000;
	ValueStack vs = new ValueStackImpl(null);
	long n1 = System.nanoTime();
	while (c-- > 0) {
		el.evaluate(vs);
	}
	long n2 = System.nanoTime();
	System.out.println((n2 - n1) );
}


而且Lite模板的JSEL本身要兼容javascript运算规则,本来就是一个不公平的竞争。


请注意,是1000万次,而非100万次。

10 楼 dennis_zane 2010-07-05 09:23
jindw 写道
jindw 写道
貌似性能改经空间依然很大很大呢。
我在我普通笔记本上试了一下Lite 模板表达式计算你的第一个实例:000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71
消耗时间只有2031104791纳秒(2秒多点)

@Test
public void testEL() {
	Expression el = ExpressionFactoryImpl.getInstance().create(
				"1000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71");
	int c = 100*10000;
	ValueStack vs = new ValueStackImpl(null);
	long n1 = System.nanoTime();
	while (c-- > 0) {
		el.evaluate(vs);
	}
	long n2 = System.nanoTime();
	System.out.println((n2 - n1) );
}


而且Lite模板的JSEL本身要兼容javascript运算规则,本来就是一个不公平的竞争。


请注意,是1000万次,而非100万次。
9 楼 jindw 2010-07-05 02:02
jindw 写道
貌似性能改经空间依然很大很大呢。
我在我普通笔记本上试了一下Lite 模板表达式计算你的第一个实例:000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71
消耗时间只有2031104791纳秒(2秒多点)

@Test
public void testEL() {
	Expression el = ExpressionFactoryImpl.getInstance().create(
				"1000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71");
	int c = 100*10000;
	ValueStack vs = new ValueStackImpl(null);
	long n1 = System.nanoTime();
	while (c-- > 0) {
		el.evaluate(vs);
	}
	long n2 = System.nanoTime();
	System.out.println((n2 - n1) );
}


而且Lite模板的JSEL本身要兼容javascript运算规则,本来就是一个不公平的竞争。
8 楼 jindw 2010-07-05 01:59
貌似性能改经空间依然很大很大呢。
我在我普通笔记本上试了一下Lite 模板表达式计算你的第一个实例:000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71
消耗时间只有2031104791纳秒(2秒多点)

@Test
public void testEL() {
	Expression el = ExpressionFactoryImpl.getInstance().create(
				"1000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71");
	int c = 100*10000;
	ValueStack vs = new ValueStackImpl(null);
	long n1 = System.nanoTime();
	while (c-- > 0) {
		el.evaluate(vs);
	}
	long n2 = System.nanoTime();
	System.out.println((n2 - n1) );
}

7 楼 ffwuchang 2010-07-02 08:40
好东西,有机会用一下
6 楼 thebest 2010-07-01 21:23
有没有类似好的C语言的库?
5 楼 littleJava 2010-07-01 09:21
一直在用MVEL,
虽然还有freemarker,但是在表达式上弱于MVEL,
另:MVEL也支持模板
4 楼 chinakite 2010-07-01 09:06
有点意思,赞一个,不管是程序还是文笔都赞
3 楼 superhanliu 2010-07-01 08:40
好东西,要是有机会用用还是很不错的。
2 楼 zjumty 2010-06-30 21:40
不知道和MVEL比起来怎么样.
http://mvel.codehaus.org/Performance+of+MVEL+2.0
1 楼 hu437 2010-06-30 20:10
看看,对于做指标计算或者数据填报类的系统还是需要的~~~

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • Java表达式引擎Aviator.zip

    Aviator是一个高性能、轻量级的基于java实现的表达式引擎,它动态地将String类型的表达式编译成Java ByteCode并交给JVM执行。 Aviator支持所有的关系运算符和算术运算符,不支持位运算,同时支持表达式的优先级,...

  • Aviator-开源轻量级、高性能的表达式求值器

    一、轮子的必要性 表达式的求值上,java的选择非常多,强大的如Groovy、JRuby,N年没维护的... 我将Aviator定位在Groovy这样全功能的脚本和IKExpression这样的简易的表达式求值之间的东西,如果你不希望带上Gro...

  • Aviator——开源轻量级、高性能的表达式求值器 (转)

    一、轮子的必要性  表达式的求值上,java的选择非常多,强大的如Groovy、JRuby,N年没维护... 我将Aviator定位在Groovy这样全功能的脚本和IKExpression这样的简易的表达式求值之间的东西,如果你不希望带上Groovy...

  • Aviator——轻量级Java表达式求值引擎

    Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要Avaitor呢? Aviator的设计目标是轻量级和高性能 ...

  • Aviator 表达式引擎 轻量级的java语言实现的表达式求值引擎

    文章目录Aviator 表达式引擎 轻量级的java语言实现的表达式求值引擎`Process` 简介`Process` 实现原理`Process` 特性`...Aviator 是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态

  • AVIATOR——轻量级JAVA表达式求值引擎

    Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要Avaitor呢?Aviator的设计目标是轻量级和高性能 ,相比于...

  • JAVa开源表达式_Aviator 表达式求值引擎开源框架 | 学步园

    简介¶Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要Avaitor呢?Aviator的设计目标是轻量级和高性能,...

  • java aviator_Aviator 表达式求值引擎开源框架

    简介¶Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要Avaitor呢?Aviator的设计目标是轻量级和高性能,...

  • Google Aviator——轻量级 Java 表达式引擎实战

    Drools(JBoss Rules )是一个开源业务规则引擎,符合业内标准,速度快、效率高。业务分析师或审核人员可以利用它轻松查看业务规则,从而检验是否已编码的规则执行了所需的业务规则。

  • 【java表达式引擎】四、高性能、轻量级的AviatorScript

    高性能、轻量级的AviatorScript

  • Java表达式求值引擎Aviator(一)

    Aviator 是一个高性能、轻量级的 java 语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的 java 表达式求值引擎,为什么还需要 Avaitor 呢? Aviator 的设计目标是轻量级和高性能 ...

  • Aviator 一种表达式求值引擎

    Aviator是一个高性能、轻量级的 java 语言实现的表达式求值引擎, 主要用于各种表达式的动态求值。现在已经有很多开源可用的 java 表达式求值引擎,为什么还需要 Avaitor 呢? Aviator的设计目标是轻量级和高性能,相比...

  • 轻量级Java表达式引擎Aviator

    Aviator是一个高性能、轻量级的 java 语言实现的表达式求值引擎, 主要用于各种表达式的动态求值。现在已经有很多开源可用的 java 表达式求值引擎,为什么还需要 Avaitor 呢? Aviator的设计目标是轻量级和高性能,相比...

  • groovy 规则引擎 java_Drools, IKExpression, Aviator和Groovy字符串表达式求值比较

    [Gradle是Android开发新一代的 Build System, 也是 Android Studio默认的build工具。Gradle脚本是基于一种...规则引擎的最基本的功能就是计算表达的值(表达式是规则中的基础部分),为了选择一个合适的基础构件作表...

  • google aviator表达式求值引擎

    Aviator是一个高性能、轻量级的 java 语言实现的表达式求值引擎, 主要用于各种表达式的动态求值。现在已经有很多开源可用的 java 表达式求值引擎,为什么还需要 Avaitor 呢? Aviator的设计目标是轻量级和高性能,相比...

  • MATLAB实现多种群遗传算法(完整代码)

    1.程序功能说明: MATLAB实现多种群遗传算法(完整代码) 逼近C=[9,8,7,6,5,4,3,2,1]的9维向量. 2.代码说明:注释清晰,参数和变量说明清晰,方便更改、方便初学者使用,模块化编程,方便替换目标函数。运行环境Windows7及以上操作系统,MATLAB2014a及以上版本。 3.适用专业:计算机、电子信息工程、数学、物理、机械工程、土木工程等专业的大学生、研究生毕业设计,各类专业的课程设计、海外留学生作业等。 4作者介绍:资深算法工程师, 从事Matlab、Python算法仿真工作15年,专业研究遗传算法、粒子群算法、蚁群算法、鲸鱼算法、狼群算法等. 有问题联系QQ: 1579325979

  • 小区物业管理系统 免费JAVA毕业设计 2024成品源码+论文+数据库+启动教程.zip

    免费JAVA毕业设计 2024成品源码+论文+数据库+启动教程 启动教程:https://www.bilibili.com/video/BV1SzbFe7EGZ 项目讲解视频:https://www.bilibili.com/video/BV1Tb421n72S 二次开发教程:https://www.bilibili.com/video/BV18i421i7Dx

  • 前后端分离爱家商城 免费JAVA毕业设计 2024成品源码+论文+录屏+启动教程.zip

    免费JAVA毕业设计 2024成品源码+论文+录屏+启动教程 启动教程:https://www.bilibili.com/video/BV1SzbFe7EGZ 项目讲解视频:https://www.bilibili.com/video/BV1Tb421n72S 二次开发教程:https://www.bilibili.com/video/BV18i421i7Dx

  • 小学元旦晚会PPT 模版

    小学元旦晚会PPT 模版

Global site tag (gtag.js) - Google Analytics