现代Java应用架构越来越强调数据存储和处理的分离,以实现更好的可维护性、可扩展性和可移植性,如热门微服务典型。这种架构通常要求业务逻辑在Java程序中实现,而不是像传统应用程序架构那样放在数据库中。应用程序中的大部分业务逻辑都涉及结构化数据处理。数据库(SQL)对这类任务有丰富的支持,可以相对容易地实现业务逻辑。然而,Java一直缺乏这样的基础支持,导致用Java实现业务逻辑非常繁琐,效率低下。这样一来,虽然在架构上有各种优势,但是开发效率却大幅下降。如果我们在Java中也提供一套完整的结构化数据处理和计算类库,那么这个问题就可以迎刃而解:即可以在不降低开发效率的情况下享受架构的优势。需要什么样的能力?一个理想的Java下结构化数据处理类库应该具备哪些特点?我们可以从SQL中总结一下:1.聚合计算能力结构化数据往往是批量(以集合的形式)出现的。为了方便地计算此类数据,需要提供足够的集合计算能力。如果没有集合操作类库,只有数组的基本数据类型(相当于集合),我们需要写四五行循环语句来对集合成员进行简单求和,完成过滤等操作,分组和聚合。在数百行代码中。SQL提供了丰富的集合操作,例如SUM/COUNT等聚合操作,WHERE过滤,GROUP分组,还支持集合的交、并、差等基本操作。这样写出来的代码会短很多。2、Lambda语法有集合运算能力就够了吗?如果我们为Java开发一批集合操作库,是否可以达到SQL的效果呢?没那么简单!以过滤操作为例。过滤通常需要一个条件来保留满足条件的集合成员。在SQL中,这个条件以表达式的形式出现,比如写WHEREx>0,表示保留那些使x>0的计算结果为真的成员。表达式x>0不是在执行语句之前计算的,而是在遍历时为每个集合成员计算的。从本质上讲,这个表达式本质上是一个函数,一个以当前集合成员为参数的函数。对于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语法并不适合在编译型语言中实现。编译器无法判断写入参数位置的表达式是应该就地计算出表达式的值并传递下去,还是将整个表达式编译成一个函数pass,需要设计更多的语法符号来区分。但是,解释型语言没有这个问题。作为参数的表达式是先计算还是遍历集合成员后计算,可以由函数自己决定。SQL确实是一种解释型语言。SPLStream的引入是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(Orders:o,SellerId;Employees:e,EId).groups(e.Dept;sum(o.Amount))表序列支持所有结构化计算函数和计算结果也是表序列,不是Map之类的数据类型。比如对分组汇总的结果继续处理结构化数据:Orders.groups(year(OrderDate):y;sum(Amount):m).new(y:OrderYear,m*0.2:discount)是基于表序列在互联网上,SPL提供了丰富的结构化数据计算功能,如过滤、排序、分组、去重、重命名、计算列、关联、子查询、集合计算、有序计算等。这些函数具有强大的计算能力,无需硬编码辅助即可独立完成计算:组合查询:Orders.select(Amount>1000&&Amount<=3000&&like(Client,"*bro*"))排序:Orders。sort(-Client,Amount)组汇总:Orders.groups(year(OrderDate),Client;sum(Amount))内部关联:join(Orders:o,SellerId;Employees:e,EId).groups(e.Dept;sum(o.Amount))简洁的Lambda语法SPL支持接口的简单Lambda语法,无需定义函数名和函数体,可以直接使用表达式作为函数的参数,如filtering:Orders。select(Amount>1000)修改业务逻辑上不需要重构函数,只需要简单修改表达式:Orders.select(Amount>1000&&Amount<2000)SPL是一种解释型语言。使用参数表达式时,无需明确定义参数类型,使Lambda接口更简单。比如计算平方和,求和的过程中如果要计算平方,可以直观的写:Orders.sum(Amount*Amount)类似SQL,SPL语法也支持直接使用单表计算中的字段名:Orders.sort(-Client,Amount)动态数据结构SPL是一种解释型语言,天然支持动态数据结构,可以根据计算结果的结构动态生成新的表序列。特别适用于计算列、分组汇总、关联等计算,比如直接重新计算分组汇总的结果:Orders.groups(Client;sum(Amount):amt).select(amt>1000&&like(Client"*S*"))或者直接重新计算关联计算的结果:join(Orders:o,SellerId;Employees:e,Eid).groups(e.Dept;sum(o.Amount))复杂的计算通常有要拆成多个步骤,每个中间结果的数据结构几乎都不一样。SPL支持动态数据结构,无需先定义这些中间结果的结构。例如根据某年的客户付款记录表,计算每个月付款金额前10的客户:Sales2021.group(month(sellDate)).(~.groups(Client;sum(Amount):sumValue)).(~.sort(-sumValue)).(~.select(#<=10)).(~.(Client)).isect()直接执行SQLSPL也实现了SQL解释器,可以直接执行SQL,从基本的WHERE、GROUP到JOIN,甚至WITH都可以支持:$select*fromd:/Orders.csvwhere(OrderDate
