Scala 是一种有趣的语言,它一方面吸取继承了多种语言中的优秀特性,一方面又没有抛弃 Java 这个强大的平台,它运行在 Java 虚拟机(Java Virtual Machine)之上,轻松实现和丰富的 Java 类库互联互通。它即支持面向对象的编程方式,又支持函数式编程。它写出的程序像动态语言同样简洁,但事实上它倒是严格意义上的静态语言。java
目前大数据计算引擎 Spark 就是用 Scala 编写的,在 Spark 中把 Scala 的分布式、高并发、简洁等特性发挥的淋漓尽致。python
Scala 中常量以 val
定义,变量以 var
定义。val
(value)表示常量,不可修改,可是在 REPL 能够从新赋值。var
(variable)表示可变的,能够从新赋值或修改。定义变量时,可不显式指定数据类型,Scala 有很强类型自动推导功能,Scala 具备动态语言似的编写代码,但又有静态语言的编译时检查。express
val a = 10 // 定义常量
val a = 11 // a 是常量,不可修改
var c = 80 // 定义变量
c = 100
复制代码
Scala 中基础数据类型有:Byte、Short、Int、Long、Float、Double、Boolean、Char、String。和 Java 中不一样的是,Scala 中没有区分原生类型和装箱类型,如:int 和 Integer。它统一抽象成 Int 类型,这样在 Scala 中全部类型都是对象了。编程
Scala 中单引号和双引号包裹是有区别的,单引号用于字符,双引号用于字符串,并且双引号中特殊字符,需用转移字符 "\"
。此外,还有多行字面量(如3个引号)和字符串内插等特性。数组
Scala 是基于 JVM 平台,默认使用 unicode
,因此变量名能够直接使用中文。而在 Scala 中,中文也是直接显示的,性能优化
Scala 内置的控制语言有 if
、while
、for
、match case
四大主要控制语言。Scala 的控制语句都是表达式是有返回值的。并发
if (Boolean express) express
if (Boolean express) express else express
复制代码
while (Boolean express) {
statement
}
复制代码
for (identifies <- iterator)[yield] [express]
复制代码
关键字 yield
是可选的,若是表达式指定这个关键字,全部表达式的值将做为一个集合返回;若是没有指定这个关键字,则调用表达式,但不能访问其返回值。分布式
Scala 中迭代器守卫(iterator guard)ide
if (identifies <- iterator if Boolean express)[yield] [express]
复制代码
express match{
case pattern_match => express
[case statement]
}
复制代码
Scala 中的 match case 称之为模式匹配,它是默认 break 的,只要其中一个 case 语句匹配,就终止以后的全部比较。且对应 case 语句的表达值的值将做为整个 match case 表达式的返回值。函数式编程
在 Scala 中经常使用集合有:Array、List、Tuple、Set、Map等。
Scala 中各集合间的层次结构以下:
数组 Array,下标从 0 开始,经过 ()
来引用,成员可修改。
val array = Array("python", "java", "scala")
array(1) // 结果: java
array(1) = "spark" // array(1) 修改成 "spark"
复制代码
列表 List,下标从 0 开始,经过 ()
来引用,成员可读,但不可修改,List 是 Iterable 的一个子类型。
val list = List(5, 10, 20)
list(2) // 结果:20
list(1) = 30 // 报错,成员不能修改
复制代码
列表还能够经过 ::
操做符,将一个元素和列表链接起来,并把元素放在列表的开头。
1::list // List(1, 5, 10, 20)
复制代码
列表经常使用的方法:
方法 | 示例 | 说明 |
---|---|---|
contains | List(1,2,3).contains(2) | 检查列表中是否包含某个元素 |
exists | List(1,2,3).exists(_>2) | 检查列表中是否至少有一个知足条件的元素 |
forall | List(1,2,3).forall(_>2) | 检查列表中全部元素都知足条件 |
distinct | List(1,2,3,1,2).distinct | 去重复元素 |
filter | List(1,2,3).filter(_>2) | 过滤是条件为 True 的元素 |
flatten | List(List(1,2),List(3,4)).flatten | 将列表的列表变为元素列表 |
map | List(1,2,3).map(_*10) | 将函数做用于遍历集合中每一个元素获得新列表,有返回追 |
foreach | List(1,2,3).foreach(x=>println(x)) | 将函数做用于遍历集合中每一个元素,无返回值 |
reduce | List(1,2,3).reduce(_+_) | 从列表第一个元素开始从左到右规约处理 |
sortBy | List("abc"," cd", " efg") .sortBy(_.size) |
根据函数返回值进行排序 |
take | List(1,2,3,4).take(2) | 取列表前 n 个元素 |
size | List(1,2,3).size | 统计列表元素个数 |
zip | List(1,2) zip List(3,4) | 将两个列表合并为一个元组列表,列表对应索引组成一个元组 |
Scala 中采用小括号来定义元组 Tuple,下标从 1 开始,经过 _
下标来访问成员,不能经过 ()
来访问成员,元组最多支持 22 个元素。
val t = (2,5,1,4)
t._1 // 取元组第一个元素,结果:2
复制代码
Set 是一个不重复且无序的集合,初始化一个集合须要使用 Set 对象
val set = Set(1,2,2,3,4)
复制代码
Scala 中的 Map 是个可变的键/值库,建立 Map 时,指定 键-值 为元组,也可使用关系操做(->
)来指定键和值元组。与 Set、List 同样,Map 也是 Iterator 的一个子类型,支持与 List 相同操做。Map 默认是不可修改的,若是要变成能够修改的 Map,须要显式定义其类型,如 scala.collection.mutable.Map
。
val map = Map(1->"python",2->"java",3->"scala")
map(3) // 结果:scala
map += (4->"C++") // 异常,map 默认是不可变的,故添加元素报错
val map = scala.collection.mutable.Map(1->"python",2->"java",3->"scala")
map += (4->"C++")
// map 遍历
for (key<-map.keys) print(s"${key}")
for (key<-map.values) print(s"${value}")
复制代码
函数是可重用的逻辑单位,在 Scala 中,函数是一等公民,函数式编程是 Scala 的一大特点。函数能够被赋值给一个变量,也能够做为一个函数的参数被传入,甚至还能够做为函数的返回值返回。
在 Scala 中使用 def
关键词来定义一个函数,定义函数通常须要肯定函数的名称、参数和函数体,具体格式以下:
def <func_name>(<param>:<type>[,...]):type=<expression>
复制代码
若是有参数,须要说明参数类型,若是函数不是递归,就不必定须要指明返回类型,Scala 在编译的时候能够根据等号右侧的表达式的类型推导出返回类型。
若是函数体有多条语句,能够放在 {}
中,最后一句的值为函数的返回值,有时须要用 return
显式指定返回值,并退出函数。
def fun1(x:Int) = if(x>0) x else -x // 不指明返回值类型
def fun2(x:Int):Int = if(x>0) x else -x // 指明返回值类型
def f1() = "hello"
def f2 = "ok"
def f3 = "scala"
复制代码
在 Scala 中有匿名函数,匿名函数是 Scala 的重要特色之一,语法格式为:
([<param1>:<type>,...])=><expression>
复制代码
示例:
var a = (x:Int)=> x+2
var y = a(10) // 结果:12
复制代码
函数也是对象,能够把它视为参数化表达式块,而表达式块是能够递归或嵌套,因此函数自己也能够递归或嵌套。
递归会形成堆栈的大量占用,可使用尾递归进行优化。优化的方式是,在函数定义前加上@annotation.tailrec
这个注解来声明尾递归,当编译器检测一个函数调用是尾递归时,它就覆盖当前的活动记录而不是在栈中去建立一个新的,只有最后一个语句为递归调用的函数才能由 Scala 编译器完成尾递归优化,不然编译时将报错。
def fn(n:Int):Int={
if (n<=0) 1
else n*fn(n-1)
}
// 尾递归
@annotation.tailrec
def fn(n:Int, m:Int):Int={
if (n<=0) m
else fn(n-1, m*n)
}
复制代码
在 Scala 中有带默认参数的函数,使用了默认参数,当在调用函数的过程当中能够输入该参数,也能够不输入该参数,若是没有传递参数,则会调用它的默认参数值,若是传递了参数,则传递值会取代默认参数。
def addInt(x:Int,y:Int=0):Int = {
x+y
}
addInt(20) // 使用默认参数,结果:20
addInt(20,30) // 使用传递的参数,结果:50
复制代码
在 Scala 中有变长参数函数,只须要在该函数参数类型后增长一个星号(*
)
def sum(items:Int*):Int = {
var sum = 0
for(i <- items) sum += i
sum
}
sum(1,2,3)
sum(1,2,3,4)
复制代码
能够在调用函数参数后面追加 :_*
,:_*
操做符将高速编译器把序列(Array,List,Seq,Vector等)中的每一个元素做为一个单独的参数传给函数。
val list = List(1,2,3,4,5)
sum(list:_*) // 结果:15
复制代码
调用函数时,一般须要制定函数中的全部参数(函默认值的参数除外),若是参数较多,并且调用是但愿保留部分参数值,而修改部分参数值,能够在原函数基础上定义一个部分应用函数便可。
def addItems(x:Int, y:Int):Int = x+y
val addItems10 = addItems(10, _:Int)
addItems10(30) // 结果:40
复制代码
实现部分应用函数,还有一种更简便的作法,采用多个参数表的函数,将一个参数表分红应用参数和非应用参数而是应用其中一个参数表的重参数,不用另外一个参数表中的参数,这种技术称为柯里化(currying)。
def additems(x:Int)(y:Int):Int = x+y
val addItems10 = addItems(10)_
addItems10(30) // 结果:40
复制代码
好文推荐: