Scala 是一种运行在 JVM上的函数式的面向对象语言。
正常情况下 Scala 语句以不断行结尾(不需要分号),为了节省带宽/存储等,Scala 也支持将多行代码写在同一行,此时语句需要使用分号来结束,最后一条代码的分号可以省略。
// 一条语句独占一行
println("Hello World!")
// 多条语句在一行
println("Hello World!"); println("Hello Scala!")
Scala 可以根据变量的值自动推断变量的类型
官方推荐使用 val:
Scala 中可以使用插值表达式来定义字符串,能有效避免大量字符串的拼接。
val|var 变量名 = s"${变量|表达式}字符串"
首次使用该值时,才会被加载
lazy val 变量名 = 表达式
lazy 只支持 val,不支持 var。
数值类型自动类型转换从小到大为:Byte,Short,Char,Int,Long,Float,Double。
val|var 变量名 = 值.toXxx // 例如强转为 Int 则为 toInt
导包: import scala.io.StdIn
调用方法:通过 StdIn.readXxx() 接收用户键盘录入的数据
Scala 中使用 {} 表示一个语句块,语句块是有返回值的(最后一个逻辑行)。
if 语句在使用时,要注意的事项有以下三点:
// 定义变量,表示年龄
var age = 18;
// 定义常量,接收 if 语句的返回值
val result = if (age >= 18) "已成年" else "未成年"
println("age:" + age + "," + result)
for (i <- 表达式|数组|集合) {
// 语句块(也叫循环体)
}
for 表达式中,可以添加 if 判断语句,这个 if 判断就称之为守卫。通过守卫可以让 for 表达式更加简洁。
for (i <- 表达式|数组|集合 if 表达式) {
// 语句块
}
yield 是一个类似 return 的关键字,但是 yield 不会结束函数,而 return 会结束函数。如果在循环结构中使用了 yield,相当于迭代一次遇到 yield 时就将 yield 后面(右边)的值放入一个集合,最后整个循环结束时将集合返回。我们把使用了 yield 的 for 表达式称之为推导式。yield不仅可以使用于 for 循环中,还可以使用于某个函数的参数,只要这个函数的参数允许被迭代.
// 将 1~10 的偶数返回
val result = for (i <- 1 to 10 if i % 2 == 0) yield i
println(result)
// 生成 10,20,...,90,100
val result = for (i <- 1 to 10) yield i * 10
println(result)
注意:下一次迭代时,从上一次迭代遇到的 yield 后面的代码(下一行)开始执行。
在 Scala 中,类似 Java 的 break 和 continue 关键字被移除了,如果一定要使用 break/continue,需要使用 scala.util.control 包下的 Breaks 类的 breakable 块和 break 方法。
在 Scala 中,方法传参默认是 val 类型,即不可变,这意味着你在方法体内部不能改变传入的参数。这和 Scala 的设计理念有关,Scala 遵循函数式编程理念,强调方法不应该有副作用。
def 方法名(参数名:参数类型, 参数名:参数类型, ...): [返回值类型] = {
// 语句块(方法体)
}
在 Scala 中,定义方法时返回值类型可以省略,由 Scala 编译器自动推断。但是定义递归方法时,不能省略。否则会报错: error:recursive method accumulation needs result type 。
当记录方法返回值的变量被声明为 lazy 时,方法的执行将会被推迟,直到我们首次使用该值时,方法才会执行。这样的方法叫做惰性方法。
注意:lazy 只支持 val,不支持 var。
Scala 中的方法参数比较灵活,支持以下几种类型:
在参数类型后面加一个 * 号,表示参数可以是 0 个或者多个
一个方法有且只能有一个变长参数,并且变长参数要放到参数列表的最后边
后缀调用法
中缀调用法
花括号调用法
无括号调用法
方法只有一个参数时,才能使用花括号调用法。
如果方法没有参数,可以省略方法名后面的括号。
在 Scala 中,如果方法的返回值类型为 Unit 类型,这样的方法称之为过程(Procedure)。
Scala 混合了面向对象特性和函数式的特性。在函数式编程中,函数是一等公民,主要体现在:
函数式编程(Functional Programming,FP)FP 是编程范式之一,常见的编程范式还有面向过程编程、面向对象编程。所谓的函数式编程指的是方法的参数列表可以接收函数对象,函数式编程中的函数指的不是程序中的函数(方法),而是数学中的函数即映射关系。
// 因为函数是对象,所以函数有类型:(函数参数类型1, 函数参数类型2,...) => 函数返回值类型
val 函数名: (函数参数类型1, 函数参数类型2,...) => 函数返回值类型 = (参数名:参数类型, 参数名:参数类型, ...) => {
函数体
}
结论:在 Scala 中,函数是对象,而方法是属于对象的,所以可以理解为:方法归属于函数。
val|var 变量名 = 方法名 _
在 Scala 中,我们返回某些数据时,可以返回一个 Option 类型的对象来封装具体的数据,从而有效的避免空指针异常。
Some(x) :表示实际的值
None :表示没有值
使用 getOrElse 方法,当值为 None 时可以指定一个默认值
如果类是空的,没有任何成员,可以省略 {}
如果构造器的参数为空,可以省略 ()
Scala 提供了一个简洁的初始化成员变量的方式,使用 _
可以让代码看起来更加简洁、优雅。
Scala没有 public 关键字,任何没有被标为 private 或 protected 的成员都是公共的。
Scala 中的权限修饰符只有:private、private[this]、protected、默认这四种。
当创建对象的时候,会自动调用类的构造器。
class 类名(val|var 参数名:类型 = 默认值, val|var 参数名:类型 = 默认值, ...) {
// 语句块(构造代码块)
}
在 Scala 中,除了定义主构造器外,还可以根据需要来定义辅助构造器。我们把除了主构造器之外的构造器称为辅助构造器。
def this(参数名:类型, 参数名:类型) {
// 第一行需要调用主构造器或者其他构造器
// 辅助构造器代码
}
辅助构造器的第一行代码,必须调用主构造器或者其他辅助构造器。
cala 中是没有 static 关键字的,要想定义类似于 Java 中的 static 变量、static 方法,就要使用 Scala 中的单例对象,也就是 object。单例对象除了没有构造器外,可以拥有类的所有特性。
main 方法必须在一个单例对象中
单例对象表示全局仅有一个对象,也叫孤立对象。定义单例对象和定义类很像,就是把 class 换成 object。
object 单例对象名{}
在 object 中定义的成员属性类似于 Java 的静态属性,在内存中都只有一个
在单例对象中,可以直接使用 单列对象名. 的形式调用成员
一个 class 和 一个 object 具有相同的名字时,这个 object 就被称为伴生对象,这个 class 被称为伴生类。
如果某个成员的权限设置为 private[this] ,则表示只能在当前类中访问,即使是伴生对象也无法访问。
在 Scala 中,创建对象的时候可以省去 new 关键字,这种写法可以让代码看起来更加简洁、优雅。但是,必须通过伴生对象的 apply 方法来实现。
object 伴生对象名 {
def apply(参数名:参数类型, 参数名:参数类型, ...) = new 类(参数1, 参数2, ...)
}
val 对象名 = 伴生对象名(参数1, 参数2, ...)
unapply 方法相当于 apply 方法的逆过程,对对象进行解构,快速提取对象属性。主要作用是和 match 模式匹配一起使用。
class|object A类 extends B类 {
}
// 判断对象是否为指定类型
val trueOrFalse:Boolean = 对象.isInstanceOf[类]
// 将对象转换为指定类型
val 变量 = 对象.asInstanceOf[类型]
sInstanceOf 只能判断对象是否为指定类型的继承链上的对象,而不能精确的判断出是哪个对象。如果要求精确的判断出对象的类型,只能使用 getClass 和 classOf 来实现。
// 定义抽象类
abstract class 抽象类名 {
// 定义抽象属性
val|var 抽象属性名:类型
// 定义抽象方法
def 方法名(参数:参数类型, 参数:参数类型, ...): 返回类型
}
new 类名() {
// 重写父类中所有的抽象内容
}
注意:上述格式中,如果类的主构造器参数列表为空,则小括号可以省略不写。
在不影响当前继承体系的情况下,对某些类或者某些对象的功能进行增强。
注意:只有抽象内容的特质被称为瘦接口,既有抽象内容又有具体内容的特质被称为富接口。
定义特质
trait 特质名称 {
// 具体属性
// 抽象属性
// 具体方法
// 抽象方法
}
继承特质
class 类 extends 特质1 with 特质2 {
// 重写抽象属性
// 重写抽象方法
}
Scala 中不管是类还是特质,都使用继承 extends 关系,特质支持多继承
如果要继承多个特质,则特质之间使用 with 关键字隔开
通过使用特质的对象混入功能,在不改变类继承体系的情况下,对对象的功能进行临时增强或者扩展。
所谓的对象混入指的是:在 Scala 中,类和特质之间无任何的继承关系,但是通过特定的关键字,却可以让该类的对象具有指定特质中的成员。
val|var 对象名 = new 类 with 特质
在 Scala 中,特质也可以继承类,继承后特质会将类中的非私有成员都继承下来。
class 类A {
// 成员属性
// 成员方法
}
trait B extends A {
}
格式一:文件顶部标记法,合并版。
package com.yjxxt.packages
// 这里写类,特质等
格式二:文件顶部标记法,分解版。
package com.yjxxt
package packages
// 这里写类,特质等
格式三:串联式包语句。
package com.yjxxt {
package packages {
// 这里写类,特质等
}
}
包中可以定义子包,也可以定义类或者特质,但是 Scala 中不允许直接在包中定义变量或者方法,这是因为 JVM 的局限性导致的,要想解决此问题,就需要使用包对象。
在 Scala 中,每个包都有一个包对象,包对象的名称和包名必须一致,且它们之间是平级关系,不能嵌套定义。
package 包名1 {
package 包名2 {
}
// 包名 2 的包对象
package object 包名2 {
}
在 Scala 中,我们也是可以通过访问权限修饰符来限定包中一些成员的访问权限。
访问权限修饰符[包名] // 例如:private[com] var name = "张三"
在 Scala 中,导入包也是通过关键字 import 来实现,但是 Scala 中的 import 功能更加强大,更加灵活,它不再局限于编写到 Scala 文件的顶部,而是可以编写到 Scala 文件中任何你需要的地方。且 Scala 默认引入了 java.lang 包,scala 包,以及 Predef 包。
在 Scala 中,样例类是一种特殊的类,一般用于保存数据(类似 Java 的 POJO 类)
case class 样例类名(val|var 成员属性1:类型1, 成员属性2:类型2, ...) {}
注意:val|var 可以不写,不写时默认为 val。
apply() :
toString() :
equals() :
hashCode() :
copy() :
unapply() :
除此之外,样例类默认还继承了 scala.Serializable 特质。
在 Scala 中,用 case 修饰的单例对象叫做样例对象。样例对象没有主构造器,它主要用在两个地方:
枚举类:为了统一项目的某些参数的值而定义的类。
没有任何参数的消息传递:目前先了解即可,后续 Akka 并发编程时再详细讲解。
case object 样例对象名 {}
在 Scala 中,用 case 修饰的单例对象叫做样例对象。样例对象没有主构造器,它主要用在两个地方:
枚举类:为了统一项目的某些参数的值而定义的类。
没有任何参数的消息传递:目前先了解即可,后续 Akka 并发编程时再详细讲解。
case object 样例对象名 {}
Scala 混合了面向对象和函数式的特性,在函数式编程中,函数是一等公民,主要体现在:
常用的高阶函数包含:
在 Scala 中,函数和 Int、String、Class 等其他类型处于同等的地方,可以像其他类型的变量一样被传递和操作。也就是说,一个函数的参数列表可以接收函数对象。
在 Scala 中,函数和 Int、String、Class 等其他类型处于同等的地方,可以像其他类型的变量一样被传递和操作。所以函数也可以作为返回值。
当函数只用一次,无需特意声明再调用,直接通过匿名函数就可以实现。
// 无参匿名函数
() => {}
// 有参匿名函数
(参数1:参数类型, 参数2:参数类型, ...) => {}
x => {}
(x: Int) => {}
(x: Int, y: String) => {}
回调函数(Callback Function)就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
偏应用函数(Partial Applied Function)也叫部分应用函数
val 函数名 = (参数1:参数类型, 参数2:参数类型, 参数3:参数类型, ...) => {
函数体
}
// 参数 1 已知
val 偏应用函数名 = (2, 参数2, 参数3, ...) {}
// 参数 1 和 2 已知
val 偏应用函数名 = (2, 5, 参数3, ...) {}
柯里化(Currying)函数,是指将原来接受多个参数的方法转换为多个只有一个参数的参数列表的函数。即:有多个参数列表,或者说多个小括号括起来的参数列表的函数就是柯里化函数。
func(x, y, z) -> func(x)(y, z) // 对第一个参数柯里化
func(x, y, z) -> func(x, y)(z) // 对最后一个参数柯里化
func(x, y, z) -> func(x)(y)(z) // 全柯里化
柯里化不会调用函数,它只是对函数进行转换
实现柯里化的方式有两种:
柯里化让我们能够更容易地获取偏应用函数。普通函数在被柯里化之后,当我们调用它的时候传入一个参数或两个参数时,它会返回偏应用函数。
闭包(Closure)是一个存在内部函数的引用关系,该引用指向的是外部函数的局部变量(前提是内部函数使用了外部函数的局部变量)。简单的理解闭包,可以分为以下几点:
通常我们执行一个函数,它都是无状态的
闭包即一个函数对象,即使函数对象的调用在它原始作用域之外,依然能够访问在它词法作用域内的变量。
定义阶段:我们定义了 Function 作为闭包,但是却没有真正执行它。由于我们只是定义了闭包,而没有执行,所以 num 对象是不存在的。
创建阶段:这时候,我们真正执行了 outer 闭包的内容,并返回执行结果,num 被创建出来。这时候,只要 func 不被 GC,那么
num 也会一直存在。
访问阶段:然后我们可以通过某种方式访问 outer 闭包中的内容(本例中间接访问了 num)。
所有集合的抽象特质:
特质不可变的实现类(蓝色为抽象类,黑色为具体实现类):
集合库中所有类的图示(蓝色为接口,黑色为具体实现类):
可变集合比不可变集合功能更加丰富,例如:Seq 集合中增加了 Buffer(ArrayBuffer、ListBuffer) 可变集合
当我们接触一个新的继承体系时,建议采用学顶层,用底层的方式:
Traversable 是一个特质(trait),它是其他集合的父特质,它的子特质 immutable.Traversable 和 mutable.Traversable 分别是不可变集可变集合的父特质,集合中大部分通用的方法都是在这个特质中定义的。
格式一:创建空的 Traversable 对象
// 方式一:通过 empty 方法实现
val t1 = Traversable.empty[Int]
// 方式二:通过小括号方式实现
val t2 = Traversable[Int]()
// 方式三:通过 Nil 实现
val t3 = Nil
格式二:创建带元素的 Traversable 对象
// 方式一:通过 toTraversable() 方式实现
val t4 = List(1, 2, 3).toTraversable
val t5 = Array(1, 2, 3).toTraversable
val t6 = Set(1, 2, 3).toTraversable
// 方式二:通过 Traversable 的伴生对象的 apply() 方法实现
val t7 = Traversable(1, 2, 3)
转置集合:transpose()
方法
拼接集合:每 ++
一次,就会创建一个新的临时集合,concat()
计算阶乘:scan()
判断元素:
forall()
:如果集合中所有元素都满足指定的条件则返回 true,否则返回 falseexists()
:只要集合中任意一个元素满足指定的条件就返回 true,否则返回 falseIterable 代表一个可以迭代的集合,它继承了 Traversable 特质,同时也是其他集合的父特质。最重要的是,它定义了获取迭代器 iterator 的方法: def iterator: Iterator[A] ,这是一个抽象方法,它的实现类需要实现这个方法,从而实现迭代集合并返回集合中的元素。
Traversable 提供了两种遍历数据的方式:
如果遇到将 Iterable 对象中的元素分成固定大小的组,然后再遍历的需求,可以通过 grouped() 方法来实现。
def grouped(size: Int): Iterator[Ierable[A]]
Iterable 集合中存储的每个元素都是有索引的,如果想按照 元素 -> 索引 这种格式生成一个新的集合,可以使用 zipWithIndex() 方法。
sameElements() 方法:判断两个集合中的元素是否全部相同,而且还要求这两个集合元素的迭代顺序保持一致
Seq(Sequence)特质代表按照一定顺序排列的元素序列,序列是一种特别的可迭代集合,它的元素特点是有序(元素存取顺序一致),可重复,有索引
IndexedSeq 和 LinearSeq 是 Seq 的子特质
IndexedSeq 代表索引序列,相对于 Seq 来说,它并没有增加额外的方法,对于随机访问元素,它更加有效,常用的子集合有:NumericRange、Range、Vector、String 等。
LinearSeq 代表线性序列,它通过链表的方式实现,因此它的 head、tail、isEmpty 执行起来相对更高效,常用的子集合有:List、Stack、Queue 等。
Stack :表示栈数据结构,元素特点是先进后出。由于历史原因,Scala 当前的库中还包含一个。
immutable.Stack ,但当前已经被标记为弃用,因为它的设计不怎么优雅,而且性能也不太好,因为栈会涉及到大量元素的进出,所以不可变栈的应用场景还是比较少,最常用的还是可变栈。
Queue :表示队列数据结构,元素特点是先进先出。
上述方法都是查找到指定数据后则返回对应的索引,如果找不到则返回 -1。
updated
:修改指定索引位置的元素
patch
:修改指定区间的元素
Set 集合是没有重复元素的集合,所有的元素都是唯一的。
Map 表示映射,是以键值对的方式存储数据(key-value),键不能重复,但是值可以有重复。根据键得到值,对 Map集合遍历时先得到键的 Set 集合,再对 Set 集合进行遍历,得到相应的值。
集合遍历时先得到键的 Set 集合,再对 Set 集合进行遍历,得到相应的值。常用子类有以下几种:
数组就是用来存储多个元素(类型可以不同,使用时一般存储相同类型的数据)的容器,每个元素都有编号,被称为下标或者索引,编号从 0 开始。
数组的长度不允许改变,数组的内容可以改变。
格式一:通过指定长度定义数组。
val|var 数组名 = new Array[元素类型](数组长度)
格式二:通过指定元素定义数组。
val|var 数组名 = Array(元素1, 元素2, 元素3, ...)`
数组的长度和内容都是可变的,可以往数组中添加、删除元素。
创建变长数组,需要先导入 import scala.collection.mutable.ArrayBuffer
。
格式一:创建空的 ArrayBuffer 变长数组。
val|var 变量名 = ArrayBuffer[元素类型]()
格式二:创建带有初始元素的 ArrayBuffer 变长数组
val|var 变量名 = ArrayBuffer(元素1, 元素2, 元素3, ...)
增删改
元组一般用来存储多个不同类型的值,例如同时存储姓名、年龄、性别、出生年月等数据,就要用到元组来存储了。元组的长度和元素都是不可变的。
格式一:通过小括号实现。
val|var 元组名 = (元素1, 元素2, 元素3, ...)
格式二:通过箭头来实现
val|var 元组名 = 元素1 -> 元素2
格式二只适用于元组中只有两个元素的情况。
在 Scala 中可以通过 元组名._编号
的形式来访问元组中的元素, _1
表示访问第一个元素,依次类推。也可以通过元组名.productIterator
的方式来获取该元素的迭代器,从而实现遍历元组。
格式一:访问元组中的单个元素。
元组名._1 // 第一个元素
元组名._2 // 第二个元素
元组名._N // 第N个元素
格式二:遍历元组。
val tuple = (元素1, 元素2, 元素3, ...)
val iterator = tuple.productIterator
for (elem <- iterator) println(elem)
列表 List 是 Scala 中最重要也是最常用的一种数据结构。它的特点是:有序,可重复
有序的意思并不是会自动排序,而且值元素的插入顺序和取出顺序是一致的。可重复表示列表中可以添加相同的元素多次
不可变列表指的是:列表的元素、长度都是不可变的。
语法格式
格式一:通过 List() 直接初始化。
val|var 列表名 = List(元素1, 元素2, 元素3, ...)
格式二:通过 Nil 创建一个空列表。
val|var 列表名 = Nil
格式三:通过 :: 创建列表。
val|var 列表名 = 元素1 :: 元素2 :: 元素n :: Nil
使用 :: 方式创建的列表,必须在最后添加一个 Nil 。
可变列表指的是列表的元素、长度都是可变的。
语法格式
创建可变列表,需要先导入 import scala.collection.mutable.ListBuffer
格式一:创建空的可变列表。
val|var 列表名 = ListBuffer[数据类型]()
格式二:通过 ListBuffer() 直接初始化。
val|var 列表名 = ListBuffer(元素1, 元素2, 元素3, ...)
可变列表的常见操作
基本上容器都有 toXxx 方法,方便容器之间进行转换。元组需要先获取到迭代器再转换。
扁平化是指将嵌套列表中的每个元素单独放到一个新的列表中。
如果某个列表中的所有元素都是列表,那么这样的列表就被称为:嵌套列表。
列表转换成其对应的字符串形式,可以通过 toString 方法或者 mkString 方法实现
union :
表示对两个列表进行并集(合并)操作,且不去重
例如: list1.union(list2) ,表示获取 list1 和 list2 中所有的元素(元素不去重)
intersect :
表示对两个列表取交集(相同)
例如: list1.intersect(list2) ,表示获取 list1,list2 中都有的元素
diff :
表示对两个列表取差集(不同)
例如: list1.diff(list2) ,表示获取 list1 中有,但是 list2 中没有的元素
集 Set 也是 Scala 中常用的一种数据结构。它的特别是:唯一,自动去重。
无序的意思是插入顺序和取出顺序是不一致的。唯一表示集中的元素具有唯一性不可重复。
不可变集指的是:集的元素、长度都是不可变的。
语法格式
格式一:创建一个空的不可变集。
val|var 集名 = Set[类型]()
格式二:给定元素来创建一个不可变集。
val|var 集名 = Set(元素1, 元素2, 元素3, ...)
可变集指的是集的元素、长度都是可变的。
语法格式
创建方式与不可变集一致,只不过创建可变集,需要先导入 import scala.collection.mutable.Set
。
格式一:创建一个空的不可变集。
val|var 集名 = mutable.Set[类型]()
格式二:给定元素来创建一个不可变集。
val|var 集名 = mutable.Set(元素1, 元素2, 元素3, ...)
映射指的就是 Map,它是由键值对(Key Value)组成的集合。特点是:键具有唯一性,值可以重复。
如果添加相同键元素,则后者的值会覆盖前者的值。
不可变 Map 指的是:Map 的元素、长度都是不可变的。
语法格式
格式一:通过箭头的方式实现。也就意味着 Map 内部是一个个的二元组。
val|var map = Map(键1 -> 值1, 键2 -> 值2, 键3 -> 值3, ...)
格式二:通过小括号(元组)的方式实现。
val|var 集名 = Map((键1, 值1), (键2, 值2), (键3, 值3), ...)
可变 Map 指的是集的元素、长度都是可变的。
语法格式
创建方式与不可变 Map 一致,只不过创建可变 Map,需要先导入 import scala.collection.mutable.Map
。
格式一:通过箭头的方式实现。
val|var map = mutable.Map(键1 -> 值1, 键2 -> 值2, 键3 -> 值3, ...)
格式一:通过箭头的方式实现。
val|var 集名 = mutable.Map((键1, 值1), (键2, 值2), (键3, 值3), ...)
Scala 针对每一类集合都提供了一个迭代器,用来迭代访问集合。
使用 iterator 方法可以从集合获取一个该集合的迭代器,迭代器中有两个方法:
Range 属于区间类型,本质上就是一种特殊的 Array。
to:左闭右闭(左右都包含)
until:左闭右开(左边包含右边不包含),以下两行代码效果一致
by:设置步长,让一个数值在每次运算中加上某个数(此即步长)重复执行此项运算
不可变栈的应用场景还是比较少,最常用的还是可变栈。
Queue 表示队列数据结构,元素特点是先进先出
集合常用函数如下:
def foreach(f:(A) => Unit): Unit
// 简写形式
def foreach(函数)
除了使用 Set 集对元素进行去重外,还可以使用 distinct 对元素进行去重。将数据集中重复的数据去重。其实distinct 底层就是用了 HashSet。
def map[B](f: (A) => B): TraversableOnce[B]
// 简写形式
def map(函数对象)
扁平化映射可以理解为先 map,然后再 flatten。
首先将函数作用于集合中的每个元素,然后将结果展平,返回新的集合。
def flatMap[B](f: (A) => GenTraversableOnce[B]): TraversableOnce[B]
// 简写形式
def flatMap(f: (A) => 待转换的集合的处理代码)
def filter(f: (A) => Boolean): TraversableOnce[A]
sorted :按默认规则(升序)排序集合,如果需要降序,升序后 reverse 反转即可。
sortBy :按指定属性排序集合,对集合元素根据传入的函数转换后,再进行排序。
def sortBy[B](f: (A) => B): List[A]
// 简写形式
def sortBy(函数对象)
sortWith :按自定义规则排序集合,根据一个自定义的函数(规则)来进行排序。
def sortWith(f: (A, A) => Boolean): List[A]
// 简写形式
def sortWith(函数对象,表示自定义的比较规则)
def groupBy[K](f: (A) => K): Map[K, List[A]]
// 简写形式
def groupBy(f: (A) => 具体的分组代码)
聚合操作是指将一个集合中的数据合并为一个值
reduce :对集合元素进行聚合计算(将集合传入一个函数进行聚合计算),reduce 又分为:
reduce:从左到右计算
reduceLeft:从左到右计算
reduceRight:从右到左计算
def reduce[A1 >: A](op: (A1, A1) => A1): A1
// 简写形式
def reduce(op:(A1, A1) => A1)
fold :对集合元素进行折叠计算,fold 又分为:
fold:从左往右计算
foldLeft:从左往右计算
foldRight:从右到左计算
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1
// 简写形式
def fold(初始值)(op:(A1, A1) => A1)
泛型的意思是泛指某种具体的数据类型,在 Scala 中,泛型用 [数据类型]
表示。
泛型 T 可以是任意字母,一般使用 T 来定义,表示 type 类型的意思。
使用 T <: 类型
表示给类型添加一个上界,表示泛型参数必须是继承自该类型或是其类型本身。
例如: [T <: Animal]
表示泛型 T 的数据类型必须是 Animal 类型或者 Animal 的子类型。
使用 T >: 类型
表示给类型添加一个下界,表示泛型参数必须是该类型的祖先类或是其类型本身。
例如: [T >: Person]
表示泛型 T 的数据类型必须是 Person 类型或者 Person 的祖先类型。
一个模式匹配包含了一系列备选项,每个备选项都开始于关键字 case
。且每个备选项都包含了一个模式及一到多个表达式。箭头符号 =>
隔开了模式和表达式。
变量 match {
case "常量1" => 表达式1
case "常量2" => 表达式2
case "常量n" => 表达式n
case _ => 表达式n+1
}
match 表达式还可以进行类型匹配。如果我们要根据不同的数据类型,来执行不同的逻辑,也可以使用 match 表达式来实现。
对象名 match {
case 变量名1:类型1 => 表达式1
case 变量名2:类型2 => 表达式2
case 变量名n:类型n => 表达式n
case _ => 表达式n+1
}
如果 case 表达式中不会使用匹配时的变量,可以使用下划线来代替。
所谓的守卫是指在 case 语句中添加 if 条件判断
变量 match {
case 变量名 if条件 => 表达式
...
case _ -> 表达式
}
Scala 中还可以使用模式匹配来匹配样例类,从而实现快速获取样例类中的成员。
对象名 match {
case 样例类型1(属性1, 属性2, 属性n) => 表达式1
case 样例类型2(属性1, 属性2, 属性n) => 表达式2
case 样例类型n(属性1, 属性2, 属性n) => 表达式n
case _ => 表达式n+1
}
Scala 的模式匹配还可以用来匹配数组、列表、元组、集、映射等。
match 中不支持直接匹配集,可以通过转为数组或列表的方式进行匹配。
由于 Map 中的元素就是一个一个的二元组,所以在遍历时,可以使用元组匹配。