- 一、Scala 简介
- 1.1 什么是 Scala
- 1.2 为什么使用 Scala
- 二、Scala 环境搭建
- 2.1 程序编译执行流程
- 2.2 安装 Scala SDK
- 2.3 安装 IDEA scala 插件
- 三、Scala 基础语法
- 3.1 变量与常量
- 3.2 字符串
- 3.3 数据类型与操作符
- 数据类型
- 操作符
- Scala 类型层次结构
- 四、流程控制
- 4.1 条件表达式
- if 表达式
- 块表达式
- 4.2 循环
- for 表达式
- while 循环
- break 和 continue
- 五、方法和函数
- 5.1 方法
- 方法定义
- 方法参数
- 方法调用方式
- 5.2 函数
- 定义函数
- 方法和函数的区别
- 方法转换为函数
- 偏函数
- 六、集合
- 6.1 数组
- 定长数组
- 变长数组
- 遍历数组
- 数组的常用算法
- 6.2 元组
- 定义元组
- 访问元组
- 6.3 列表
- 不可变列表
- 可变列表
- 列表常用操作
- 6.4 集合
- 不可变集
- 可变集
- 6.5 映射
- 6.6 Option 类型
- 6.7 iterator 迭代器
- 七、函数式编程
- 7.1 遍历(foreach)
- 7.2 映射(map)
- 7.3 映射扁平化(flatmap)
- 7.4 过滤(filter)
- 7.5 是否存在(exists)
- 7.6 排序(sorted、sortBy、sortWith)
- 7.7 分组(groupBy)
- 7.8 聚合计算(reduce)
- 7.9 折叠(fold)
Scala 是运行在 JVM 上的多范式编程语言,同时支持面向对象和面向函数编程。
- 面向对象特性:
- 所有变量都是对象
- 对象的数据类型和行为由类和特征描述
- 利用特征实现混入式多重继承
- 函数式编程:
- 每个函数都是一个值
- 支持高阶函数
- 开发大数据应用程序(Spark 程序、Flink 程序)
- 表达能力强,一行代码抵得上 Java 多行,开发速度快
- 兼容 Java,可以访问庞大的 Java 类库
- Java 程序编译执行流程
- Scala 程序编译执行流程
所以,要编译运行 scala 程序,需要 - jdk(jvm)
- scala 编译器(scala SDK)
- Windows
步骤:- 从scala 官网中下载安装包
- 安装在指定目录,例如:d:/tools
- 配置环境变量:
增加系统环境变量 SCALA_HOME=安装路径,
修改系统环境变量path=%SCALA_HOME%bin;... - 测试是否安装成功:打开控制台,输入scala -version
- Linux
步骤:- 从scala 官网中下载安装包
- 上传到 linux 服务器指定目录下并解压
- 配置环境变量:
执行vim /etc/profile编辑配置脚本
增加环境变量export SCALA_HOME=安装路径
在 Path 后追加 scala 的安装路径export PATH=$PATH:...:$SCALA_HOME/bin - 测试是否安装成功:在 linux 终端输入scala -version
IDEA 默认是不支持 scala 程序开发,所以需要安装 scala 插件来支持 scala 语言
- 离线安装
步骤:- 到 IDEA 官网中下载与 IDEA 对应版本的IDEA scala 插件
- 在 IDEA 启动页面中,选择 Configure->Plugins
- 在弹出的页面中点击右上角的小齿轮,选择 Install Plugin from Disk 选择插件安装包
- 在 Installd 中显示了 scala 插件,表示安装成功
- 重启 IDEA
- 在线安装
- 在 IDEA 菜单栏中选择 File->Settings->Plugins,进入到插件市场
- 搜索 scala,显示 scala 插件,点击 Install 进行安装
- 在 Installd 中显示了 scala 插件,表示安装成功
- 重启 IDEA
- 变量
var 变量名:类型 = 初始值 // 类型可不写,scala会自动根据变量的值来推断变量的类型
- 常量(常用)
val 变量名:类型 = 初始值 // 类型可不写,scala会自动根据变量的值来推断变量的类型
- 惰性赋值
当有一些变量保存的数据较大时(比如 SQL 语句),不需要马上加载到 JVM 内存,可以使用惰性赋值来提高效率。
lazy val/var 变量名 =
"""
|
|select ...
|from 表
|
|""".stripMargin
3.2 字符串
Scala 提供了多种定义字符串的方式
- 使用双引号
val/var 变量名 = "字符串"
- 使用插值表达式
// 适用于大量字符串的拼接
val/var 变量名 = s"$变量"
val/var 变量名 = s"${变量/表达式}"
- 使用三引号
// 适用于保存大段的文本
val/var 变量名 = """
|
|select ...
|from 表
|
|"""
3.3 数据类型与操作符
数据类型
Java 类型与Scala 的区别:
1. Scala 中所有的类型都使用大写字母开头
2. 整型使用 Int 而不是 Integer
3. Scala 中定义变量可以不写类型,Scala 编译器会自动推断
- Scala 中没有 ++、-- 运算符,可以使用 +=、-= 进行运算
- Scala 可以直接使用 ==、!=、equals 进行值的比较,而比较两个对象的引用值(即地址值),则使用 eq 方法
| 类型 | 说明 |
|---|---|
| Any | 所有类型的父类,它有两个子类 AnyVal 与 AnyRef |
| AnyVal | 所有数值类型的父类 |
| AnyRef | 所用对象类型(引用类型)的父类 |
| Unit | 表示无值,Unit 是 AnyVal 的子类,它只有一个实例(),类似于 Java 中的 void |
| Null | Null 是 AnyRef 的子类,它的实例是 null,可以将 null 赋值给任何对象类型 |
| Nothing | 所有类型的子类,不能直接创建该类型实例,某个方法抛出异常时,返回的就是 Nothing 类型。因为 Nothing 是所有类的子类,那么它可以赋值为任何类型 |
- 在 Scala 中,条件表达式也是有返回值的
- 在 Scala 中,没有三元表达式,可以使用 if 表达式替代三元表达式
val sex = "male" val result = if(sex == "male") 1 else 0 println(result) // 1块表达式
- 在 Scala 中,使用 {} 表示一个块表达式
- 和 if 表达式一样,块表达式也是有值的
- 值就是最后一个表达式的值
语法:
for(i <- 表达式/数组/集合) {
// 表达式
}
for(i <- 1 to 5) println(i) // 包含 5 for(i <- 1 until 5) println(i) // 不包含 5
- 嵌套循环
for(i <- 1 to 3; j <- 1 to 5) {
println("*")
if(j == 5) println("")
}
- 守卫
在 for 表达式中添加 if 判断语句。
for(i <- 1 to 10 if i % == 0) println(i)
- for 推导式
在 for 循环体中,可以使用 yield 表达式构建出一个集合,我们把使用 yield 的 for 表达式称之为推导式。
val v = for(i <- 1 to 10) yield i * 10while 循环
Scala 中的 while 循环跟 Java 是一致的。
while(i <= 10) {
println(i)
i = i + 1
}
break 和 continue
如果要在 Scala中使用 break/continue,就需要使用 scala.util.control 包的 Break 类的 breakble 和 break 方法。
- 实现 break
用法:- 导入 Breaks 包 import scala.util.control.Breaks._
- 使用 breakable 将 for 表达式包起来
- for 表达式中需要退出循环的地方,添加 break() 方法调用
// 导入scala.util.control包下的Break
import scala.util.control.Breaks._
// 不打印大于等于50的数
breakable{
for(i <- 1 to 100) {
if(i >= 50) break()
println(i)
}
}
- 实现 continue
用法:用 breakable{} 将 for 表达式的循环体包含起来。
// 导入scala.util.control包下的Break
import scala.util.control.Breaks._
// 不打印10的倍数
for(i <- 1 to 100) {
breakable{
if(i % 10 == 0) break()
println(i)
}
}
五、方法和函数
5.1 方法
方法定义
语法:
// 参数列表的参数类型不能省略
// 返回值类型可以省略,由 scala 编译器自动推断
// 定义递归方法,不能省略返回值类型
// 返回值可以不写 return,默认就是{}块表达式的值
def methodName (参数名:参数类型, 参数名:参数类型): [return type] = {
// 方法体
}
使用:
def add(a: Int,b:Int) = a + b方法参数
- 默认参数
在定义方法时可以给参数定义一个默认值。
// x, y带有默认值为0 def add(x:Int = 0, y:Int = 0) = x + y println(add())
- 带名参数
在调用方法时,可以指定参数的名称来进行调用。
def add(x:Int = 0, y:Int = 0) = x + y println(add(x = 1))
- 变长参数
如果方法的参数是不固定的(可以是0个或多个),可以定义一个方法的参数是变长参数。
// 语法:
def 方法名(参数名:参数类型*): 返回值类型 = {
方法体
}
// 使用:
def add(num:Int*) = num.sum
println() // 0
println(add(1, 2, 3, 4, 5)) // 15
方法调用方式
- 后缀调用法
// 语法: // 对象名.方法名(参数) // 使用: Math.abs(-1)
- 中缀调用法
// 语法: // 对象名 方法名 参数 // 如果有多个参数,使用括号括起来 // 使用: Math abs -1 1 to 10
- 所有的操作符都是方法,1 + 1 也就是中缀调用法
- 如果有多个参数,使用括号括起来
- 花括号调用法
// 语法:
// 对象名.方法名{
// 表达式1
// 表达式2
// }
// 使用:
Math.abs{
println("求绝对值")
-1
}
方法只有一个参数,才能使用花括号调用法
- 无括号调用法
如果方法没有参数,调用时可以省略方法名后面的括号
def printHello() = println("hello")
printHello
5.2 函数
定义函数
// 语法: // val 函数变量名 = (参数名:参数类型, 参数名:参数类型......) => 函数体 // 使用: val add = (x:Int, y:Int) => x + y
方法和函数的区别
- 函数是一个对象(变量)
- 类似于方法,函数也有输入参数和返回值
- 函数定义不需要使用 def 定义
- 无需指定返回值类型,会自动推导
- 方法是隶属于类或者对象的,在运行时,它是加载到 JVM 的方法区中
- 可以将函数对象赋值给一个变量,在运行时,它是加载到 JVM 的堆内存中
- 函数是一个对象,继承自 FunctionN,函数对象有 apply,curried,toString、tupled 这些方法。方法则没有
用法:使用 _ 即可将方法转换为函数
def add(x:Int, y:Int) = x + y val a = add _偏函数
- 偏函数被包在花括号内没有 match 的一组 case 语句是一个偏函数
- 偏函数是 PartialFunction[A, B] 的一个实例
- A 代表输入参数类型
- B 代表返回结果类型
// 例子一:
// 1. 定义一个偏函数
val partialFunction:PartialFunction[Int, String] = {
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
// 2. 调用偏函数
println(partialFunction(1))
println(partialFunction(2))
println(partialFunction(3))
println(partialFunction(4))
// 例子二:
// 1. 定义一个列表 1-10
val list = (1 to 10).toList
// 2. 使用集合函数式操作来进行数据的转换
val stringList = list.map{
case x if x >= 1 && x <= 3 => "[1-3]"
case x if x >= 4 && x <= 8 => "[4-8]"
case _ => "(8, *)"
}
println(stringList) // List([1-3], [1-3], [1-3], [4-8], [4-8], [4-8], [4-8], [4-8], (8, *), (8, *))
六、集合
6.1 数组
定长数组
- 定长数组指的是数组的长度是不允许改变的
- 数组的元素是可以改变的
// 语法:
// 1. 通过指定长度定义数组
// val/var 变量名 = new Array[元素类型](数组长度)
// 2. 用元素直接初始化数组
//val/var 变量名 = Array(元素1, 元素2, 元素3,...)
// 使用1:
val a = new Array[Int](100)
a(0) = 100
// 使用2:
val a = Array("java", "scala", "python")
println(a.length)
变长数组
- 在 Scala 中,数组的泛型使用 [ ] 来指定
- 使用 () 来获取元素
变长数组指的是数组的长度是可变的,可以往数组中添加、删除元素。
- 定义变长数组
创建变长数组,需要提前导入 ArrayBuffer 类import scala.collection.mutable.ArrayBuffer
// 语法:
// 1. 创建空的ArrayBuffer变长数组
// val/var a = ArrayBuffer[元素类型]()
// 2. 创建带有初始元素的ArrayBuffer
// val/var a = ArrayBuffer(元素1, 元素2, 元素3,...)
// 使用1:
val a = ArrayBuffer[Int]()
// 使用2:
val a = ArrayBuffer("hadoop", "storm", "spark")
- 添加/修改/删除元素
- 使用 += 添加元素
- 使用 -= 删除元素
- 使用 ++= 追加一个数组到变长数组
val a = ArrayBuffer("hadoop", "storm", "spark")
// 追加一个元素
a += "flume" // ArrayBuffer(hadoop, storm, spark, flume)
// 删除一个元素
a -= "hadoop" // ArrayBuffer(storm, spark, flume)
// 追加一个数组
a ++= Array("hive", "sqoop") // ArrayBuffer(storm, spark, flume, hive, sqoop)
// 修改数组中的元素
a(0) = "s" // ArrayBuffer(s, spark, flume, hive, sqoop)
遍历数组
- 使用 for 表达式,直接遍历数组中的元素
val a = Array(1, 2, 3, 4, 5) for(i <- a) println(i)
- 使用索引遍历数组中的元素
val a = Array(1, 2, 3, 4, 5) for(i <- 0 to a.length - 1) println(a(i)) for(i <- 0 until a.length) println(a(i))数组的常用算法
val a = Array(4, 1, 2, 4, 10) // 求和 println(a.sum) // 21 // 求最大值 println(a.max) // 10 // 求最小值 println(a.min) // 1 // 排序 // 1. 升序排序 println(a.sorted.toList) // List(1, 2, 4, 4, 10) // 2. 反转 println(a.sorted.reverse.toList) // List(10, 4, 4, 2, 1)6.2 元组
元组可以用来包含一组不同类型的值,元组的元素是不可变的。
定义元组// 语法: // 1. 使用括号来定义元组 // val/var 元组 = (元素1, 元素2, 元素3,...) // 2. 使用箭头来定义元组(元组只有两个元素) // val/var 元组 = 元素1 -> 元素2 // 使用1: val a = (1, "linghuchong", 25, "华山派") // 使用2: val a = "linghuchong" -> 25 // (linghuchong,25) val a = "linghuchong" -> 25 -> "华山派" // ((linghuchong,25),华山派)访问元组
// 使用_1、_2、_3...来访问元组中的元素 val a = "linghuchong" -> 25 // 获取第一个元素 a._1 // linghuchong6.3 列表
列表是 Scala 中最重要的、也是最常用的数据结构。
- 列表可以保存重复的值
- 有先后顺序
不可变列表就是列表的元素、长度都是不可变的。
// 语法: // 1. 使用List(元素1, 元素2, 元素3,...)来创建一个不可变列表 // val/var 变量名 = List[列表类型]() // val/var 变量名 = List(元素1, 元素2, 元素3,...) // 2. 使用Nil创建一个不可变的空列表 // val/var 变量名 = Nil // 3. 使用::方法创建一个不可变列表 // val/var 变量名 = 元素1::元素2::Nil // 使用1: val a = List(1, 2, 3, 4) // 使用2: val a = Nil // 使用3: val a = -2 :: -1 ::Nil可变列表
- 定义
可变列表就是列表的元素、长度都是可变的。要使用可变列表,先要导入import scala.collection.mutable.ListBuffer
// 语法: // 1. 使用ListBuffer[元素类型]()创建空的可变列表 // val/var 变量名 = ListBuffer[Int]() // 2. 使用ListBuffer(元素1, 元素2, 元素3,...)创建可变列表 // val/var 变量名 = ListBuffer(元素1, 元素2, 元素3,...) // 使用1: val a = ListBuffer[Int]() // 使用2: val a = ListBuffer(1, 2, 3, 4)
- 可变集合都在 mutable 包中
- 不可变集合都在 immutable 包中(默认导入)
- 可变列表操作
- 获取元素(使用括号访问(索引值))
- 添加元素(+=)
- 追加一个列表(++=)
- 更改元素(使用括号获取元素,然后进行赋值)
- 删除元素(-=)
- 转换为List(toList)
- 转换为Array(toArray)
val a = ListBuffer(1, 2, 3) // 获取第一个元素 a(0) // 1 // 添加元素 4 a += 4 // ListBuffer(1, 2, 3, 4) // 追加一个列表,该列表包含(5, 6, 7) a ++= List(5, 6, 7) // ListBuffer(1, 2, 3, 4, 5, 6, 7) // 删除元素 7 a -= 7 // ListBuffer(1, 2, 3, 4, 5, 6) // 将可变列表转换为不可变列表 println(a.toList) // List(1, 2, 3, 5, 6, 7) // 将可变列表转换为数组 println(a.toArray)列表常用操作
- 判断列表是否为空(isEmpty)
- 拼接两个列表(++)
- 获取列表的首个元素(head)和剩余部分(tail)
- 反转列表(reverse)
- 获取前缀(take)、获取后缀(drop)
- 扁平化(flatten)
- 拉链(zip)和拉开(unzip)
- 转换字符串(toString)
- 生成字符串(mkString)
- 并集(union)
- 交集(intersect)
- 差集(diff)
val a = List(1, 2, 3)
val b = List(4, 5, 6)
val c = List(List(1, 2), List(3), List(4, 5))
// 判断列表是否为空
a.isEmpty // false
// 拼接两个列表
a ++ b // List(1, 2, 3, 4, 5, 6)
// 获取列表的首个元素
a.head // 1
// 获取列表的剩余部分
a.tail // List(2, 3)
// 反转列表
println(a.reverse) // List(3, 2, 1)
// 获取前缀:获取包含前n个元素的列表
a.take(2) // List(1, 2)
// 获取后缀:获取不包含前n个元素的列表
a.drop(2) // List(3)
// 扁平化(压平):将列表中的列表中的所有元素放到一个列表中
c.flatten // List(1, 2, 3, 4, 5)
// 拉链:将两个列表组合成一个元素为元组的列表
val names = List("linghuchong", "renyingying", "yuelingshan")
val ages = List(25, 18, 18)
val res = names.zip(ages) // List((linghuchong,25), (renyingying,18), (yuelingshan,18))
// 拉开:将一个包含元组的列表,解开成包含两个列表的元组
res.unzip // (List(linghuchong, renyingying, yuelingshan),List(25, 18, 18))
// 转换字符串
a.toString // List(1, 2, 3)
// 生成字符串:将元素以分隔符拼接起来,默认没有分隔符
a.mkString // 123
a.mkString(":") // 1:2:3
val a = List(1, 2, 3, 4)
val b = List(3, 4, 5, 6)
// 并集:不去重
a.union(b) // List(1, 2, 3, 4, 3, 4, 5, 6)
a.union(b).distinct // List(1, 2, 3, 4, 5, 6)
// 交集:
a.intersect(b) // List(3, 4)
// 差集:
// 获取a在b中不存在的元素
a.diff(b) // List(1, 2)
6.4 集合
Set(集)是代表没有重复元素的集合。
- 元素不重复
- 不保证插入顺序
- 定义
// 语法: // 1. 创建一个空的不可变集 // val/var 变量名 = Set() // val/var 变量名 = Set[类型]() // 2. 给定元素来创建一个不可变集 // var/var 变量名 = Set(元素1, 元素2, 元素3,...) // 使用1: val a = Set[Int]() // 使用2: val a = Set(1, 2, 3, 4, 5)
- 基本操作
- 获取集的大小(size)
- 遍历集(和遍历数组一样)
- 添加一个元素,生成一个Set(+)
- 拼接两个集,生成一个Set(++)
- 拼接集合列表,生成一个Set(++)
val a = Set(1, 1, 2, 3, 4, 5) // 获取集的大小 a.size // a.size // 遍历集 for(i <- a) println(i) // 5 1 2 3 4 // 删除元素1 a - 1 // Set(5, 2, 3, 4) // 添加元素1 a + 1 // Set(5, 1, 2, 3, 4) // 拼接另一个集 a ++ Set(6 ,7 ,8) // Set(5, 1, 6, 2, 7, 3, 8, 4) // 拼接一个列表 a ++ List(6, 7, 8, 9) // Set(5, 1, 6, 9, 2, 7, 3, 8, 4)可变集
可变集和不可变集的创建方式一致,只不过需要提前导入一个可变集类。要使用可变集,先要导入import scala.collection.mutable.Set
val a = Set(1, 2, 3, 4) // 添加元素5 a += 5 // Set(1, 5, 2, 3, 4) // 移除元素1 a -= 1 // Set(5, 2, 3, 4)6.5 映射
Map 可以称为映射,它是由键值对组成的集合。在 Scala 中,Map 也分为不可变 Map 和 可变 Map。
- 不可变 Map
// 语法:
// val/var map = Map(键->值, 键->值, 键->值...) // 推荐,可读性更好
val/var map = Map((键, 值), (键, 值), (键, 值)...)
// 使用:
val a = Map("zhangsan"->30, "lisi"->40)
val b = Map(("zhangsan", 30), ("lisi", 40))
a("zhangsan") // 30
- 可变 Map
定义语法与不可变 Map 一致,但定义可变 Map 需要手动导入import scala.collection.mutable.Map
val a = Map("zhangsan"->30, "lisi"->40)
a("zhangsan") = 20
a("zhangsan") // 20
6.6 Option 类型
使用 Option 类型,可以用来有效避免空指针(null)异常。将来我们返回某些数据时,可以返回一个 Option 类型来替代。
在 Scala 中,Option 类型来表示可选值。这种类型的数据有两种形式:
- Some(x):表示实际的值
- None:表示没有值
- 使用 getOrElse 方法,当值为 None 时可以指定一个默认值
// 1. 定义一个相除的方法,使用Option来封装数据
def div(x:Double, y:Double) = {
if(y == 0) {
None // 表示没有数据
} else {
val result = x / y
Some(result) // Some表示有数据
}
}
// 2. 可以使用模式匹配来判断是否有值
val some = div(10.0, 2.0)
val none = div(10.0, 0)
none match {
case Some(x) => println(x)
case None => println("除零异常")
}
// 使用getOrElse来获取Option类型的值
println(some.getOrElse(0))
println(none.getOrElse(0))
6.7 iterator 迭代器
Scala 针对每一类集合都提供了一个迭代器(iterator)用来迭代访问集合。
- 使用迭代器遍历集合
- 使用 iterator 方法可以从集合获取一个迭代器
- 迭代器的两个基本操作
- hasNext——查询容器中是否有下一个元素
- next——返回迭代器的下一个元素,如果没有,抛出 NoSuchElementException
- 每一个迭代器都是有状态的
- 迭代完后保留在最后一个元素的位置
- 再次使用则抛出 NoSuchElementException
- 可以使用 while 或者 for 来逐个返回元素
val a = List(1, 2, 3, 4, 5)
val ite = a.iterator
// while循环
while(ite.hasNext) {
println(ite.next)
}
// for表达式
// 因为上一个ite已经到了最后一个状态,所以只能重新获取一个迭代器
val ite = a.iterator
for(i<-ite) println(i)
七、函数式编程
7.1 遍历(foreach)
// 方法签名: // foreach(f: (A) => Unit): Unit val a = List(1, 2, 3, 4) // 使用: a.foreach((x:Int) => println(x)) // 使用类型推断简化函数定义 a.foreach(x => println(x)) // 使用下划线来简化函数定义:要求1.只在函数体中出现一次2.函数体没有嵌套调用 a.foreach(println(_))7.2 映射(map)
map 方法接收一个函数,将这个函数应用到每一个元素,返回一个新的列表。
// 方法签名: // def map[B](f: (A) => B): TraversableOnce[B] val a = List(1, 2, 3, 4) // 使用: a.map(x => x + 1) // List(2, 3, 4, 5) a.map(_ + 1)7.3 映射扁平化(flatmap)
可以把 flatMap 理解为先 map、然后再 flatten。
// 方法签名:
// def flatMap[B](f: (A) => GenTraversableOnce[B]: TraversableOnce[B])
val a = List("hadoop hive spark flink flume", "kudu hbase sqoop storm")
// 使:1:先map后flatten
val list = a.map(_.split(" "))
list.flatten
// 使用2:flatMap
a.flatMap(_.split(" "))
7.4 过滤(filter)
// 方法签名: // def filter(p: (A) => Boolean): TraversableOnce[A] val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9) // 使用: a.filter(_ % 2 == 0)7.5 是否存在(exists) 7.6 排序(sorted、sortBy、sortWith)
- 默认排序 sorted
val a = List(3, 1, 2, 9, 7) a.sorted // List(1, 2, 3, 7, 9)
- 指定字段排序 sortBy
// 方法签名:
// def sortBy[B](f: (A) => B): List[A]
val a = List("01 hadoop", "02 flume", "03 hive", "04 spark")
// 使用:
a.sortBy(_.split(" ")(1)) // List(02 flume, 01 hadoop, 03 hive, 04 spark)
- 自定义排序 sortWith
// 方法签名: // def sortWith(lt: (A, A) => Boolean): List[A] val a = List(2, 3, 1, 6, 4, 5) // 使用: a.sortWith((x, y) => if (x > y) true else false) // List(6, 5, 4, 3, 2, 1) a.sortWith(_ > _) //List(6, 5, 4, 3, 2, 1)7.7 分组(groupBy)
// 方法签名:
// def groupBy[K](f: (A) => K): Map[K, List[A]]
val a = List("令狐冲" -> "华山派", "岳灵珊" -> "华山派", "任盈盈" -> "日月神教", "任我行" -> "日月神教")
// 使用:
a.groupBy(x => x._2) // Map(华山派 -> List((令狐冲,华山派), (岳灵珊,华山派)), 日月神教 -> List((任盈盈,日月神教), (任我行,日月神教)))
a.groupBy(_._2)
7.8 聚合计算(reduce)
// 方法签名: // def reduce[A1 >: A](op: (A1, A1) => A1): A1 val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // 使用: a.reduce((x, y) => x + y) // 55 a.reduce(_ + _)
7.9 折叠(fold)
- reduce 和 reduceLeft 效果一致,表示从左到右计算
- reduceRight 表示从右到左计算
// 方法签名: // def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // 使用: a.fold(0)((x, y) => x + y) // 155 a.fold(100)(_ + _)
- fold 和 foldLeft 效果一致,表示从左往右计算
- fold 和 Right 表示从右往左计算



