流式结构化数据计算语言的进化与新选择


JAVA开发中经常会遇到不方便使用数据库,但又要进行结构化数据计算的场景。JAVA早期没有提供相关类库,即使排序、分组这种基本计算也要硬写代码,开发效率很低。后来JAVA8推出了Stream库,凭借Lambda表达式、链式编程风格、集合函数,才终于解决了结构化数据计算类库从无到有的问题。

Stream可以简化结构化数据的计算

比如排序:

Stream<Order> result=Orders
.sorted((sAmount1,sAmount2)->Double.compare(sAmount1.Amount,sAmount2.Amount))
.sorted((sClient1,sClient2)->CharSequence.compare(sClient2.Client,sClient1.Client));

上面代码中的sorted是集合函数,可方便地进行排序。"(参数)->函数体"的写法即Lambda表达式,可以简化匿名函数的定义。两个sorted函数连在一起用属于链式编程风格,可以使多步骤计算变得直观。

Stream计算能力还不够强

仍然以上面的排序为例,sorted函数只需要知道排序字段和顺序/逆序就够了,参考SQL的写法"…from Orders order by Client desc, Amount",但实际上还要额外输入排序字段的数据类型。顺序/逆序用asc/desc(或+/-)等符号就可以简单表示了,但这里却要用compare函数。另外,实际要排序的字段顺序和代码写出来的顺序是相反的,有些反直觉。再比如分组汇总:

Calendar cal=Calendar.getInstance();
Map<Object, DoubleSummaryStatistics> c=Orders.collect(Collectors.groupingBy(
        r->{
            cal.setTime(r.OrderDate);
            return cal.get(Calendar.YEAR)+"_"+r.SellerId;
            },
            Collectors.summarizingDouble(r->{
                return r.Amount;
            })
        )
);
    for(Object sellerid:c.keySet()){
        DoubleSummaryStatistics r =c.get(sellerid);
        String year_sellerid[]=((String)sellerid).split("_");
        System.out.println("group is (year):"+year_sellerid[0]+"/t (sellerid):"+year_sellerid[1]+"/t sum is:"+r.getSum()+"/t count is:"+r.getCount());
    }

上面代码中,所有出现字段名的地方,都要先写上表名,即"表名.字段名",而不能像SQL那样省略表名。匿名函数语法复杂,随着代码量的增加,复杂度迅速增长。两个匿名函数形成嵌套,代码更难解读。实现一个分组汇总功能要用多个函数和类,包括groupingBy、collect、Collectors、summarizingDouble、DoubleSummaryStatistics等,学习成本不低。分组汇总的结果是Map,而不是结构化数据类型,如果要继续计算,通常要定义新的结构化数据类型,并进行转换类型,处理过程很繁琐。两个分组字段在结构化数据计算中很常见,但函数grouping只支持一个分组变量,为了让一个变量代表两个字段,就要采取一些变通技巧,比如新建一个两字段的结构化数据类型,或者把两个字段用下划线拼起来,这让代码变得更加繁琐。

「Stream计算能力不足,原因在于其基础语言JAVA是编译型语言,无法提供专业的结构化数据对象,缺少来自底层的有力支持。」

JAVA是编译型语言,返回值的结构必须事先定义,遇到较多的中间步骤时,就要定义多个数据结构,这不仅让代码变得繁琐,还导致参数处理不灵活,要用一套复杂的规则来实现匿名语法。解释性语言则天然支持动态结构,还可以方便地将参数表达式指定为值参数或函数参数,提供更简单的匿名函数。

在这种情况下,Kotlin应运而生。Kotlin是基于JAVA的现代开发语言,所谓现代,重点体现在对JAVA语法尤其是Stream的改进上,即Lambda表达式更加简洁,集合函数更加丰富。

Kotlin计算能力强于Stream

比如排序:

var resutl=Orders.sortedBy{it.Amount}.sortedByDescending{it.Client}

上面代码无须指明排序字段的数据类型,无须用函数表达顺序/逆序,直接引用it作为匿名函数的默认参数,而不是刻意定义,整体比Stream简短不少。

Kotlin改进并不大,计算能力仍然不足

仍然以排序为例,Kotlin虽然提供了it这个默认参数,但理论上只要知道字段名就够了,没必要带上表名(it)。排序函数只能对一个字段进行排序,不能动态接收多个字段。

再比如分组汇总:

data class Grp(var OrderYear:Int,var SellerId:Int)
data class Agg(var sumAmount: Double,var rowCount:Int)
var result=Orders.groupingBy{Grp(it.OrderDate.year+1900,it.SellerId)}
    .fold(Agg(0.0,0),{
        acc, elem -> Agg(acc.sumAmount + elem.Amount,acc.rowCount+1)
    })
.toSortedMap(compareBy<Grp> { it. OrderYear}.thenBy { it. SellerId})
result.forEach{println("group fields:${it.key.OrderYear}/t${it.key.SellerId}/t aggregate fields:${it.value.sumAmount}/t${it.value.rowCount}") }

上面代码中,一个分组汇总的动作,需要用到多个函数,包括复杂的嵌套函数。用到字段的地方要带上表名。分组汇总的结果不是结构化数据类型。要事先定义中间结果的数据结构。

如果继续考察集合、关联等更多的计算,就会发现同样的规律:Kotlin代码的确比Stream短一些,但大都是无关紧要的量变,并未发生深刻的质变,该有的步骤一个不少。

Kotlin也不支持动态数据结构,无法提供专业的结构化数据对象,难以真正简化Lambda语法,无法脱离表名直接引用字段,无法直接支持动态的多字段计算(比如多字段排序)。

esProc SPL的出现,将会彻底改观JAVA生态下结构化数据处理的困境。

esProc SPL是JVM下的开源结构化数据计算语言,提供了专业的结构化数据对象,内置丰富的计算函数,灵活简洁的语法,易于集成的JDBC接口,擅长简化复杂计算。

本站声明:
1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;

2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;

3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;

4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;

5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/294369.html

(0)
上一篇 2022年12月1日
下一篇 2022年12月1日

相关推荐

发表回复

登录后才能评论