??作者简介:??云计算领域优质创作者??新星计划第三季python赛道TOP1??阿里云ACE认证高级工程师??
个人主页:
??个人社区:(个人社区)欢迎您的加入!
基于JVM的开源数据处理语言主要有Kotlin、Scala、SPL,下面对三者进行多方面的横向比较,从中找出开发效率最高的数据处理语言。本文的适用场景设定为项目开发中常见的数据处理和业务逻辑,以结构化数据为主,大数据和高性能不作为重点,也不涉及消息流、科学计算等特殊场景。
基本特征
适应面
Kotlin的设计初衷是开发效率更高的Java,可以适用于任何Java涉及的应用场景,除了常见的信息管理系统,还能用于WebServer、Android项目、游戏开发,通用性比较好。Scala的设计初衷是整合现代编程范式的通用开发语言,实践中主要用于后端大数据处理,其他类型的项目中很少出现,通用性不如Kotlin。SPL的设计初衷是专业的数据处理语言,实践与初衷一致,前后端的数据处理、大小数据处理都很适合,应用场景相对聚焦,通用性不如Kotlin。
编程范式
Kotlin以面向对象编程为主,也支持函数式编程。Scala两种范式都支持,面向对象编程比Koltin更彻底,函数式编程也比Koltin方便些。SPL可以说不算支持面向对象编程,有对象概念,但没有继承重载这些内容,函数式编程比Kotlin更方便。
运行模式
Kotlin和Scala是编译型语言,SPL是解释型语言。解释型语言更灵活,但相同代码性能会差一点。不过SPL有丰富且高效的库函数,总体性能并不弱,面对大数据时常常会更有优势。
外部类库
Kotlin可以使用所有的Java类库,但缺乏专业的数据处理类库。Scala也可以使用所有的Java类库,且内置专业的大数据处理类库(Spark)。SPL内置专业的数据处理函数,提供了大量时间复杂度更低的基本运算,通常不需要外部Java类库,特殊情况可在自定义函数中调用。
IDE和调试
三者都有图形化IDE和完整的调试功能。SPL的IDE专为数据处理而设计,结构化数据对象呈现为表格形式,观察更加方便,Kotlin和Scala的IDE是通用的,没有为数据处理做优化,无法方便地观察结构化数据对象。
学习难度
Kotlin的学习难度稍高于Java,精通Java者可轻易学会。Scala的目标是超越Java,学习难度远大于Java。SPL的目标就是简化Java甚至SQL的编码,刻意简化了许多概念,学习难度很低。
代码量
Kotlin的初衷是提高Java的开发效率,官方宣称综合代码量只有Java的20%,可能是数据处理类库不专业的缘故,这方面的实际代码量降低不多。Scala的语法糖不少,大数据处理类库比较专业,代码量反而比Kotlin低得多。SPL只用于数据处理,专业性最强,再加上解释型语言表达能力强的特点,完成同样任务的代码量远远低于前两者(后面会有对比例子),从另一个侧面也能说明其学习难度更低。
语法
数据类型
原子数据类型:三者都支持,比如Short、Int、Long、Float、Double、Boolean
日期时间类型:Kotlin缺乏易用的日期时间类型,一般用Java的。Scala和SPL都有专业且方便的日期时间类型。
有特色的数据类型:Kotlin支持非数值的字符Char、可空类型Any。Scala支持元组(固定长度的泛型集合)、内置BigDecimal。SPL支持高性能多层序号键,内置BigDecimal。
集合类型:Kotlin和Scala支持Set、List、Map。SPL支持序列(有序泛型集合,类似List)。
结构化数据类型:Kotlin有记录集合List,但缺乏元数据,不够专业。Scala有专业的结构化数类型,包括Row、RDD、DataSet、DataFrame(本文以此为例进行说明)等。SPL有专业的结构化数据类型,包括record、序表(本文以此为例进行说明)、内表压缩表、外存Lazy游标等。
Scala独有隐式转换能力,理论上可以在任意数据类型之间进行转换(包括参数、变量、函数、类),可以方便地改变或增强原有功能。
流程处理
三者都支持基础的顺序执行、判断分支、循环,理论上可进行任意复杂的流程处理,这方面不多讨论,下面重点比较针对集合数据的循环结构是否方便。以计算比上期为例,Kotlin代码:
mData.forEachIndexed{index,it->
if(index>0) it.Mom= it.Amount/mData[index-1].Amount-1
}
Kotlin的forEachIndexed函数自带序号变量和成员变量,进行集合循环时比较方便,支持下标取记录,可以方便地进行跨行计算。Kotlin的缺点在于要额外处理数组越界。
Scala代码:
val w = Window.orderBy(mData("SellerId"))
mData.withColumn("Mom", mData ("Amount")/lag(mData ("Amount"),1).over(w)-1)
Scala跨行计算不必处理数组越界,这一点比Kotlin方便。但Scala的结构化数据对象不支持下标取记录,只能用lag函数整体移行,这对结构化数据不够方便。lag函数不能用于通用性强的forEach,而要用withColumn之类功能单一的循环函数。为了保持函数式编程风格和SQL风格的底层统一,lag函数还必须配合窗口函数(Python的移行函数就没这种要求),整体代码看上去反而比Kotlin复杂。
SPL代码:
mData.(Mom=Amount/Amount[-1]-1)
SPL对结构化数据对象的流程控制进行了多项优化,类似forEach这种最通用最常用的循环函数,SPL可以直接用括号表达,简化到极致。SPL也有移行函数,但这里用的是更符合直觉的“[相对位置]"语法,进行跨行计算时比Kotlin的绝对定位强大,比Scala的移行函数方便。上述代码之外,SPL还有更多针对结构化数据的流程处理功能,比如:每轮循环取一批而不是一条记录;某字段值变化时循环一轮。
Lambda表达式
Lambda表达式是匿名函数的简单实现,目的是简化函数的定义,尤其是变化多样的集合计算类函数。Kotlin支持Lambda表达式,但因为编译型语言的关系,难以将参数表达式方便地指定为值参数或函数参数,只能设计复杂的接口规则进行区分,甚至有所谓高阶函数专用接口,这就导致Kotin的Lambda表达式编写困难,在数据处理方面专业性不足。几个例子:
"abcd".substring( 1,2) //值参数
"abcd".sumBy{ it.toInt()} //函数参数
mData.forEachIndexed{ index,it-> if(index>0) it.Mom=…} //函数参数的函数带多个参数
Koltin的Lambda表达式专业性不足,还表现在使用字段时必须带上结构化数据对象的变量名(it),而不能像SQL那样单表计算时可以省略表名。
同为编译型语言,Scala的Lambda表达式和Kotlin区别不大,同样需要设计复杂的接口规则,同样编写困难,这里就不举例了。计算比上期时,字段前也要带上结构化数据对象变量名或用col函数,形如mData (“Amount”)或col(“Amount”),虽然可以用语法糖弥补,写成$”Amount”或’Amo