数据结构与算法(Java版),java程序设计与数据结构基础篇
这篇文章带给你一些关于java的知识,主要是关于结构化数据处理开源库SPL的相关问题。下面我们来看看java下比较理想的结构化数据处理类库,希望对你有所帮助。
如何解决写爬虫IP受阻的问题?立即使用。
现代Java应用架构越来越强调数据存储和处理的分离,以获得更好的可维护性、可扩展性和可移植性。比如热微服务就是一个典型的例子。这种架构通常要求业务逻辑在Java程序中实现,而不是像传统的应用程序架构那样放在数据库中。
应用程序中的大多数业务逻辑都涉及结构化数据处理。数据库(SQL)对这类任务有丰富的支持,可以相对容易地实现业务逻辑。但是Java一直缺乏这种基础支持,导致用Java实现业务逻辑很繁琐,效率很低。这样一来,虽然在架构上有各种优势,但是开发效率却大幅下降。
如果我们还提供一套完整的Java结构化数据处理和计算类库,这个问题就可以解决了:既可以享受架构的优势,又不会降低开发效率。
需要什么样的能力?
Java下理想的结构化数据处理类库应该具备哪些特征?我们可以从SQL中总结出来:
1 集合运算能力
结构化数据经常批量出现(以集合的形式)。为了方便计算这类数据,需要提供足够的集合运算能力。
如果没有集合运算类库,只有array(相当于set)是基本数据类型,我们需要写四五行循环语句对集合的成员进行简单求和,筛选、分组聚合等操作就要写几百行代码。
SQL提供了丰富的集合运算,如SUM/COUNT等聚集运算,其中用于过滤,GROUP用于分组,还支持集合的交、并、差等基本运算。这样写出来的代码会短很多。
2 Lambda语法
有设定操作能力就够了吗?如果为Java开发一批集合运算类库,能达到SQL的效果吗?
没那么简单!
以过滤操作为例。筛选通常需要一个条件来保留满足该条件的集合成员。在SQL中,这个条件以表达式的形式出现,比如写WHERE x0,这意味着那些使x0的计算结果为真的成员被保留。该表达式x0不是在执行该语句之前计算的,而是在遍历过程中为每个集合成员计算的。本质上,这个表达式本质上是一个函数,以当前集合成员为参数。对于WHERE操作,相当于使用表达式定义的函数作为WHERE的参数。
这种写作有一个术语叫做Lambda语法,或者函数式语言。
如果没有Lambda语法,我们往往会临时定义函数,代码会非常繁琐,容易出现名称冲突。
Lambda语法在SQL中被广泛使用,它不仅可以用于必要的过滤和分组操作,还可以用于计算列等不必要的场景,从而大大简化了代码。
3 在 Lambda 语法中直接引用字段
结构化数据不是简单的单个值,而是有字段的记录。
我们发现,在SQL的表达式参数中引用一个记录字段时,大多数情况下,可以直接使用字段名,而不需要指明该字段所属的记录。只有当有多个同名字段时,才需要用表名(或别名)来区分。
虽然新版Java已经开始支持Lambda语法,但是它只能将当前记录作为参数传入这个由Lambda语法定义的函数中,然后在你写公式的时候总是带着这个记录。比如按单价和数量计算金额时,如果用来表示当前成员的参数名为X,则需要写成“X .单价* X .数量”的罗嗦形式。在SQL中,可以更直观地写成‘单价*数量’。
4 动态数据结构
SQL也可以很好的支持动态数据结构。
在结构化数据计算中,返回值往往是结构化数据,结果数据结构与运算有关,所以在编写代码之前是不可能准备好的。因此,有必要支持动态数据结构。
SQL中的任何SELECT语句都会产生一个新的数据结构,你可以在代码中随意添加或删除字段,而不需要事先定义结构(类)。这种Java语言不好。在代码编译阶段,应该定义所有使用的结构(类)。原则上,新结构不能在执行过程中动态生成。
5 解释型语言
从前面的文章分析,我们已经可以得出结论,Java本身并不适合作为结构化数据处理的语言。它的Lambda机制不支持特性3,作为编译语言也无法实现特性4。
实际上,上面提到的Lambda语法并不适合用编译语言实现。编译器无法确定写到参数位置的表达式是应该就地计算表达式的值然后传递,还是把整个表达式编译成函数来传递。需要设计更多的语法符号来区分它。然而,解释语言没有这个问题。作为参数的表达式是先计算还是在遍历集合成员后计算,可以由函数本身决定。
SQL确实是一种解释性语言。
引入 SPL
Stream是Java8以官方身份推出的结构化数据处理类库,但不符合上述要求。它没有专业的结构化数据类型,缺少很多重要的结构化数据计算函数,不是解释性语言,不支持动态数据类型,Lambda语法的接口复杂。
Kotlin是Java生态系统的一部分。它在Stream的基础上略有改进,还提供了结构化数据计算类型。但它不是解释性语言,不支持动态数据类型,Lambda语法的接口复杂,所以它仍然不是结构化数据计算的理想类库。
Scala提供了丰富的结构化数据计算函数,但编译语言的特性也使其不是一个理想的结构化数据计算类库。
那么,Java生态系统下还有什么可以用的呢?
收藏家SPL。
SPL是一种由Java解释和执行的程序语言。它拥有丰富的结构化数据计算类库、简单的Lambda语法和易用的动态数据结构。是Java下理想的结构化处理类库。
丰富的集合运算函数
SPL提供了专业的结构化数据类型,即顺序表。序列表和SQL的数据表一样,是批量记录的集合,具有结构化数据类型的一般功能。这里有一个例子。
解析源数据并生成序列表:
Orders=T(d:/Orders.csv )根据列名从原始有序表生成新的有序表:
Orders.new (orderid,amount,orderdate)计算列:
Orders.new (orderid,amount,year (orderdate))字段重命名为:
Orders.new (orderid: id,sellerid,year (orderdate): y)使用序列号中的字段:
Orders.groups(year(_5),_ 2;Sum(_4))序列表的名称被更改(左关联)
join@1(订单:o,SellerId员工:e,EId)。组(电子部门;Sum(o.Amount))序列表支持所有结构化的计算函数,计算结果也是序列表,而不是Map等数据类型。例如,根据分组和汇总的结果继续处理结构化数据:
orders . groups(year(OrderDate):y;总和(金额):m)。new (y: order year,m * 0.2: discount)基于序数表,SPL提供了丰富的结构化数据计算功能,如过滤、排序、分组、去重、重命名、列计算、关联、子查询、集合计算、有序计算等。这些函数具有强大的计算能力,不需要硬编码的帮助就可以独立计算:
组合查询:
orders . select(amount 1000 amount=3000 like(client, * bro * ))排序:
orders . sort(-客户,金额)分组汇总:
Orders.groups(year(OrderDate),客户端;Sum(金额))内部关联:
加入(订单:o,SellerId员工:e,EId)。组(电子部门;Sum(o.Amount))
简洁的Lambda语法
SPL支持简单的Lambda语法,不需要定义函数名和函数体,可以直接使用表达式作为函数的参数,比如过滤:
Orders.select(Amount1000)修改业务逻辑时,不需要重构函数,只需修改表达式即可:
Orders.select(金额1000金额2000) SPL是一种解释性语言。使用参数表达式时,不需要显式定义参数类型,使得Lambda接口更简单。比如在求和的过程中要计算平方和,可以直观的写出:
Orders.sum(Amount*Amount)类似于SQL,SPL语法也支持在单表计算中直接使用字段名:
Orders.sort (-client,amount):
动态数据结构
SPL是一种解释性语言,天然支持动态数据结构,可以根据计算结果结构动态生成新的订单表。特别适合列计算、分组汇总、关联计算,比如直接重新计算分组汇总结果:
订单.组(客户;总和(金额):金额)。选择(AMT 1000 like (client, * s * ))或直接重新计算关联计算结果:
加入(订单:o,SellerId员工:e,Eid)。组(电子部门;Sum(o.Amount))复杂的计算通常被拆分成多个步骤,每个中间结果的数据结构几乎是不同的。SPL支持动态数据结构,所以没有必要先定义这些中间结果的结构。例如,根据某一年的客户付款记录,计算每月付款金额为的前10名客户:
Sales2021.group(month(sellDate))。(~.团体(客户;Sum(金额):sum值)。(~.排序(-sum值))。(~.select (#=10))。(~.(客户端))。ISECT ():
直接执行SQL
SPL还实现了一个SQL的解释器,可以直接执行SQL,分组基本的WHERE,和
$select * from d:/Orders.csv其中(OrderDatedate(2020-01-01 )和Amount=100或(OrderDate=date(2020-12-31 )和Amount 100)$ select year(OrderDate),Client,sum(Amount),count(1) from d:/Orders.csv
按年份(订购日期)分组,客户
sum(Amount)=100 $ select o . OrderId,o.Client,e.Name e.Dept from d:/Orders.csv o
将d:/employees . CSV e on o . seller id=e . Eid $与t as联接(从d:/Orders.csv group by Client中选择客户,sum(amount) s)
选择t.Client、t.s、ct。姓名,ct .地址从t
左客户端表CT on t . client=CT . client
更多语言优势
SPL作为一门专业的结构化数据处理语言,不仅涵盖了SQL的全部计算能力,而且在语言方面还有更强大的优势:
离散性及其支挂下的更彻底的集合化
集合性是SQL的基本特性,即支持数据以集合的形式参与运算。但是SQL的离散性很差,集合的所有成员必须作为一个整体参与运算,不能脱离集合。但Java等高级语言支持良好的离散性,数组成员可以独立计算。
而更彻底的聚合需要离散性的支持,集合的成员可以游离出集合,随机与其他数据组成新的集合参与运算。
SPL将SQL的中心化和Java的离散化结合起来,这样可以实现更彻底的中心化。
比如在SPL很容易表达“套套”,适用于分组后计算。例如,查找所有科目的前10名学生:
0 @
| A |
---|---|
1 | =T(“score.csv”).group(subject) |
2 | =A2.(.rank(score).pselect@a(<=10)) |
3 | =A1.(~(A3(#)).(name)).isect() |
SPL序表的字段可以存储记录或记录集合,这样可以用对象引用的方式,直观地表达关联关系,即使关系再多,也能直观地表达。比如,根据员工表找到女经理下属的男员工: |
|
具体来说,SPL可以通过绝对位置来引用成员。比如取3阶可以写成Orders(3),取1、3、5记录可以写成Orders([1、3、5])。
SPL还可以通过相对位置引用成员,例如计算每条记录相对于前一条记录的增长率:orders . derivative(amount/amount[-1]-1)
SPL也可以用#来表示当前记录的序号。例如,根据序列号将雇员分为两组,一组是奇数序列号,另一组是偶数序列号:Employees.group(#%2==1)
更方便的函数语法
大量强大的结构化数据计算功能。这是好事,但是会造成功能相近的函数难以区分。无形中提高了学习难度。
SPL为函数选项提供了独特的语法。功能相似的函数可以共用一个函数名,仅用函数选项来区分不同。例如,select函数的基本功能是过滤。如果只过滤掉第一个符合条件的记录,只需使用选项@1:
Orders.select@1(Amount1000)数据量大的时候可以用并行计算来提升性能,改成optio就行了
Orders.select@m(Amount1000)用于排序数据,使用二分法快速筛选,使用@b:
Orders.select@b(Amount1000)功能选项也可以组合搭配,比如:
Orders.select@1b(Amount1000)结构化运算函数的参数往往比较复杂。比如SQL需要使用各种关键字将一条语句的参数分成多个组,但这样会使用很多关键字,使语句结构不一致。
SPL支持层次参数。参数由高到低用分号、逗号、冒号分为三层,从总体上简化了复杂参数的表达:
加入(订单:o,SellerIdEmployees:e,EId)
扩展的Lambda语法
普通的Lambda语法不仅要表示表达式(即函数形式的参数),还要完整定义表达式本身的参数,否则在数学形式上不够严格,导致Lambda语法繁琐。例如,用循环函数select过滤集合A,只保留值为偶数的成员。一般形式是:
A.select(f(x):{x%2==0})这里的表达式是x%2==0,表达式的参数是f(x)中的x,表示集合A中的成员,也就是循环变量。
SPL使用固定符号~代表循环变量。当参数是循环变量时,不需要定义参数。在SPL,上面的Lambda语法可以简单地写成:A.select(~ %2==0)
普通的Lambda语法必须定义表达式中使用的每个参数。除了循环变量,常用的参数是循环计数。如果在Lambda中也定义了循环计数,代码会更复杂。
SPL使用固定符号#代表循环计数变量。例如,使用函数select过滤集合A,只保留偶数的成员。SPL可以写:A.select(# %2==0)
相对位置往往出现在难计算中,相对位置本身也很难计算。用相对位置的时候,参数写的会很繁琐。
SPL固定形式[序号]代表相对位置:
Ken1@166.com:
无缝集成、低耦合、热切换
作为Java解释的脚本语言,SPL提供了JDBC驱动,可以无缝集成到Java应用中。
简单的语句可以像SQL一样直接执行:
…
class . forname( com . esproc . JDBC . internal driver );
connection conn=driver manager . get connection( JDBC:esproc:local://);
prepare statement ST=conn . prepare statement(=T(\ D:/orders . txt \ )。select(Amount 1000 Amount=3000 like(Client,\ * S * \ )));
ResultSet结果=ST . execute();
.复杂的计算可以保存为脚本文件,并作为存储过程调用。
…
class . forname( com . esproc . JDBC . internal driver );
connection conn=driver manager . get connection( JDBC:esproc:local://);
语句st=连接。();
callable statement ST=conn . prepare call( { call spl script 1(?)});
st.setObject(1,3000);
ST . setobject(25000);
ResultSet结果=ST . execute();
.将脚本放在Java程序之外,一方面可以减少代码耦合,另一方面可以利用解释和执行的特性支持热切换。当业务逻辑发生变化时,可以通过修改脚本立即生效,不像使用Java时,往往需要重启整个应用程序。这种机制特别适合在微服务架构中编写业务处理逻辑。
推荐:《java视频教程》以上是帮助你了解Java结构化数据处理开源库SPL的详细内容。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。