阅读更多

    《飞行大亨》是我很喜欢的一部电影,不过这里我想介绍的是一个叫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的设计目标是轻量级和高性能,相比...

  • 前18大旋转修整器企业占据全球87%的市场份额.docx

    前18大旋转修整器企业占据全球87%的市场份额

  • Planet-SkySat-Imagery-Product-Specification-Jan2020.pdf

    SKYSAT IMAGERY PRODUCT SPECIFICATION PLANET.COM VIDEO Full motion videos are collected between 30 and 120 seconds by a single camera from any of the active SkySats. Videos are collected using only the Panchromatic half of the camera, hence all videos are PAN only. Videos are packaged and delivered with a video mpeg-4 file, plus all image frames with accompanying video metadata and a frame index file (reference Product Types below)

  • Screenshot_20240506_133458_com.netease.yhtj.vivo.jpg

    Screenshot_20240506_133458_com.netease.yhtj.vivo.jpg

  • 2019年A~F题特等奖论文合集.pdf

    大学生,数学建模,美国大学生数学建模竞赛,MCM/ICM,历年美赛特等奖O奖论文

Global site tag (gtag.js) - Google Analytics