Scala 提供了一套功能极其丰富、设计优雅的集合库,并鼓励使用高阶函数来声明式地处理数据,这不仅使代码更简洁、易读,也是编写高性能分布式计算任务 (如 Spark) 的核心思想
思维导图
一、数组
概述: 数组是一种容器,用于存储多个相同类型的元素,每个元素都有一个从0开始的索引。
类型: Scala有两种数组类型:
a. 定长数组 (Array): 长度不可变,但元素内容可变。
scala
// 创建方式1: 指定长度,使用默认零值初始化
val arr1 = new Array[Int](5) // 创建一个包含5个0的整数数组
// 创建方式2: 直接指定元素
val arr2 = Array("hello", "scala", "world")
// 访问与修改元素
println(arr2(0)) // 输出: hello
arr2(0) = "hi" // 修改第一个元素
println(arr2(0)) // 输出: hi
b. 变长数组 (ArrayBuffer): 长度和内容都可变,可以高效地添加或删除元素。
scala
import scala.collection.mutable.ArrayBuffer
// 创建变长数组
val buf1 = new ArrayBuffer[Int]()
val buf2 = ArrayBuffer("a", "b", "c")
// 增删元素
buf1 += 1 // 追加单个元素
buf1 += (2, 3) // 追加多个元素
buf1 ++= Array(4, 5) // 追加一个数组
println(buf1) // 输出: ArrayBuffer(1, 2, 3, 4, 5)
buf1 -= 5 // 移除单个元素
buf1 --= Array(1, 2) // 移除多个元素
println(buf1) // 输出: ArrayBuffer(3, 4)
c. 常用操作与算法:
scala
val arr = Array(3, 1, 4, 1, 5, 9, 2, 6)
println(s"Length: ${arr.length}")
println(s"Sum: ${arr.sum}")
println(s"Max: ${arr.max}")
println(s"Sorted: ${arr.sorted.mkString(", ")}") // sorted 返回新数组
println(s"Reversed: ${arr.reverse.mkString(", ")}") // reverse 返回新数组
// 遍历
for (elem <- arr) {
print(s"$elem ")
}
println()
二、元组
概述: 元组通常用于存储多个不同类型的值,其长度和元素都不可变。它是一个轻量级的数据聚合方式。
创建与访问:
scala
// 创建方式1: 使用小括号
val t1 = (1, "hello", 3.14)
// 创建方式2: 使用箭头 (只适用于2个元素的元组,常用于Map)
val t2 = "id" -> 101
// 访问元素: 通过 _N 访问,N从1开始
println(t1._1) // 输出: 1
println(t1._2) // 输出: hello
println(t2._2) // 输出: 101
// 遍历 (不常用)
t1.productIterator.foreach(println)
三、列表
概述: 列表是一种有序、可重复的数据结构。Scala中的 List
默认是 不可变 的,这是函数式编程的基石。
a. 不可变列表 (List):
scala
// 创建方式
val list1 = List(1, 2, 3, 4)
val list2 = 1 :: 2 :: 3 :: Nil // :: 是右结合操作符,用于在列表头部添加元素
val emptyList = Nil
// 常用操作 (所有操作都返回一个新的列表)
val list3 = list1 :+ 5 // 在尾部追加元素: List(1, 2, 3, 4, 5)
val list4 = 0 +: list1 // 在头部添加元素: List(0, 1, 2, 3, 4)
val list5 = list1 ++ list2 // 连接两个列表
println(list1.head) // 获取第一个元素: 1
println(list1.tail) // 获取除第一个元素外的所有元素: List(2, 3, 4)
println(list1.take(2)) // 获取前2个元素: List(1, 2)
println(list1.drop(2)) // 丢弃前2个元素: List(3, 4)
val nestedList = List(List(1, 2), List(3, 4))
println(nestedList.flatten) // 扁平化: List(1, 2, 3, 4)
val keys = List("a", "b")
val values = List(1, 2)
println(keys.zip(values)) // 拉链: List((a,1), (b,2))
b. 可变列表 (ListBuffer):
如果确实需要一个可变的列表,可以使用 ListBuffer
。
scala
import scala.collection.mutable.ListBuffer
val buffer = ListBuffer(1, 2, 3)
buffer += 4
buffer.remove(0) // 按索引删除
println(buffer) // 输出: ListBuffer(2, 3, 4)
// 转换为不可变列表
val finalList = buffer.toList
四、集
概述: Set
是一种没有重复元素的集合,其特点是元素唯一且无序。
a. 不可变集 (Set):
scala
val set1 = Set(1, 2, 3, 3, 4) // 重复的3会被自动去除
println(set1) // 输出: Set(1, 2, 3, 4)
// 常用操作 (返回新集)
val set2 = set1 + 5 // 添加元素
val set3 = set1 - 3 // 删除元素
val set4 = set1 ++ Set(4, 5, 6) // 合并集
println(set1.contains(3)) // 检查是否包含元素: true
b. 可变集 (mutable.Set):
scala
import scala.collection.mutable.Set
val mutableSet = Set("a", "b", "c")
mutableSet += "d"
mutableSet.remove("a")
println(mutableSet) // 输出: Set(b, c, d)
五、映射
概述: Map
由键值对 (key, value) 组成,键是唯一的,而值可以重复。
a. 不可变映射 (Map):
scala
// 创建方式
val map1 = Map("a" -> 1, "b" -> 2, "c" -> 3)
val map2 = Map(("a", 1), ("b", 2))
// 获取值
println(map1("a")) // 直接访问,如果键不存在会抛异常
println(map1.get("d")) // get返回Option类型: None
println(map1.getOrElse("d", 0)) // getOrElse提供默认值: 0
println(map1.keys) // 获取所有键
println(map1.values) // 获取所有值
b. 可变映射 (mutable.Map):
scala
import scala.collection.mutable.Map
val mutableMap = Map("x" -> 10, "y" -> 20)
mutableMap("z") = 30 // 增加或更新键值对
mutableMap.remove("x") // 删除键
println(mutableMap) // 输出: Map(y -> 20, z -> 30)
六、迭代器
概述: Scala为每种集合都提供了迭代器,用于遍历集合元素。
重要方法: hasNext
(检查是否有下一个元素) 和 next
(返回下一个元素并移动指针)。
注意事项: 迭代器是有状态的,一旦遍历完成,就不能再使用。
代码案例:
scala
val list = List(1, 2, 3)
val it = list.iterator // 获取迭代器
while (it.hasNext) {
println(it.next())
}
// 再次使用 it.next() 会抛出 NoSuchElementException
七、函数式编程
概述: 函数式编程是指方法的参数列表可以接收函数对象。Scala集合库内置了大量高阶函数,让数据处理极其优雅。
下表总结了最常用的高阶函数:
函数 (Function) | 语法/示例 | 作用 |
---|---|---|
foreach |
list.foreach(println) |
遍历集合,对每个元素执行指定操作 (无返回值)。 |
map |
list.map(x => x * 2) |
将集合中的每个元素进行转换,返回一个新的集合。 |
flatMap |
list.flatMap(x => List(x, x+1)) |
结合了 map 和 flatten ,先映射成集合,再压平。 |
filter |
list.filter(x => x % 2 == 0) |
筛选出满足条件的元素,返回一个新的集合。 |
sorted / sortBy / sortWith |
list.sortBy(x => -x) |
提供多种排序方式:默认排序、指定字段排序、自定义比较函数排序。 |
groupBy |
list.groupBy(x => if (x%2==0) "even" else "odd") |
按指定条件对元素进行分组,返回一个Map。 |
reduce |
list.reduce((a, b) => a + b) |
对集合元素进行聚合计算 (从左到右),将多个元素合并为一个。 |
fold |
list.fold(100)((a, b) => a + b) |
类似于 reduce ,但可以提供一个初始值。 |
代码案例:
scala
val numbers = List(1, 2, 3, 4, 5)
// map: 每个元素乘以2
val doubled = numbers.map(_ * 2) // _ 是 (x => x * 2) 的简写
println(doubled) // List(2, 4, 6, 8, 10)
// filter: 只保留偶数
val evens = numbers.filter(_ % 2 == 0)
println(evens) // List(2, 4)
// reduce: 计算所有元素的和
val sum = numbers.reduce(_ + _)
println(sum) // 15
// groupBy: 按奇偶分组
val grouped = numbers.groupBy(n => if (n % 2 == 0) "even" else "odd")
println(grouped) // Map(odd -> List(1, 3, 5), even -> List(2, 4))
八、综合案例
这个案例将综合运用本节所学的多种集合和函数式操作。
代码实现:
scala
// 1. 使用 case class 定义数据模型
case class Student(id: Int, name: String, major: String, score: Double)
// 2. 创建一个学生列表 (List)
val students = List(
Student(1, "Alice", "CS", 88.5),
Student(2, "Bob", "Math", 95.0),
Student(3, "Charlie", "CS", 76.0),
Student(4, "David", "Physics", 91.2),
Student(5, "Emily", "Math", 89.8)
)
// 3. 使用 filter 找出所有 CS 专业且分数高于 80 的学生
println("--- CS students with score > 80 ---")
val highScorersCS = students.filter(s => s.major == "CS" && s.score > 80)
highScorersCS.foreach(println)
// 4. 使用 map 提取所有学生的姓名
println("\n--- All student names ---")
val names = students.map(_.name)
println(names)
// 5. 使用 groupBy 按专业对学生进行分组
println("\n--- Students grouped by major ---")
val studentsByMajor = students.groupBy(_.major)
studentsByMajor.foreach { case (major, studentList) =>
println(s"Major: $major, Students: ${studentList.map(_.name).mkString(", ")}")
}
// 6. 使用 map 和 reduce 计算每个专业的平均分
println("\n--- Average score per major ---")
val avgScores = studentsByMajor.map { case (major, studentList) =>
val totalScore = studentList.map(_.score).reduce(_ + _)
(major, totalScore / studentList.length)
}
println(avgScores)
// 7. 使用 sortBy 找出分数最高的学生
println("\n--- Top student ---")
val topStudent = students.sortBy(_.score).reverse.head
println(topStudent)
练习题
题目一:定长数组
创建一个包含5个整数的定长数组,并将其第三个元素 (索引为2) 的值修改为100。
题目二:变长数组
创建一个变长数组 (ArrayBuffer),初始包含字符串 "a", "b", "c"。然后,向其尾部添加 "d",并移除头部的 "a"。
题目三:元组
创建一个元组,用于存储一个用户的ID (Int)、姓名 (String) 和是否为VIP (Boolean)。然后,访问并打印出该用户的姓名。
题目四:不可变列表
使用 ::
和 Nil
创建一个包含整数 3, 2, 1 的不可变列表。
题目五:列表操作
给定一个列表 val list = List(1, 2, 3, 4, 5)
,写一行代码生成一个新列表,该新列表包含原列表除前两个元素外的所有元素,并且在末尾追加了数字 6。
题目六:不可变集
创建一个不可变集,包含数字 1, 2, 3, 4, 5。然后,写一行代码生成一个新集,它是在原集的基础上移除了数字 3 并添加了数字 6。
题目七:不可变映射
创建一个不可变映射,存储两个城市及其人口: "Beijing" -> 2154, "Shanghai" -> 2428 (单位:万)。然后,安全地获取 "Tokyo" 的人口,如果不存在,则返回 0。
题目八:可变映射
创建一个可变映射,初始为空。然后,向其中添加键值对 "apple" -> 5,并更新 "apple" 的值为 10。
题目九:map
函数
给定 val names = List("alice", "bob", "charlie")
,使用 map
函数生成一个新列表,其中所有名字都首字母大写。
题目十:filter
函数
给定 val numbers = List(-1, 0, 5, -10, 20)
,使用 filter
函数筛选出所有的正数。
题目十一:flatMap
函数
给定 val sentences = List("hello world", "hello scala")
,使用 flatMap
获取所有句子中的单词,并去重。
题目十二:reduce
函数
给定 val numbers = List(1, 2, 3, 4, 5)
,使用 reduce
函数计算所有数字的乘积。
题目十三:fold
函数
给定 val numbers = List(1, 2, 3)
,使用 fold
函数计算 100 - 1 - 2 - 3
的结果。
题目十四:groupBy
函数
给定 val words = List("apple", "banana", "avocado", "blueberry", "cherry")
,使用 groupBy
函数按照每个单词的首字母进行分组。
题目十五:综合应用
给定一个包含多个元组的列表 val data = List(("a", 1), ("b", 2), ("a", 3))
。请编写一行代码,使用 groupBy
, map
, sum
来计算每个键 (如 "a", "b") 对应的所有值的总和。
答案与解析
答案一:
scala
val arr = new Array[Int](5)
arr(2) = 100
答案二:
scala
import scala.collection.mutable.ArrayBuffer
val buf = ArrayBuffer("a", "b", "c")
buf += "d"
buf.remove(0) // or buf -= "a"
答案三:
scala
val user = (101, "Alice", true)
println(user._2)
答案四:
scala
val list = 3 :: 2 :: 1 :: Nil
答案五:
scala
val list = List(1, 2, 3, 4, 5)
val newList = list.drop(2) :+ 6
答案六:
scala
val set1 = Set(1, 2, 3, 4, 5)
val newSet = set1 - 3 + 6
答案七:
scala
val populations = Map("Beijing" -> 2154, "Shanghai" -> 2428)
val tokyoPop = populations.getOrElse("Tokyo", 0)
答案八:
scala
import scala.collection.mutable.Map
val scores = Map[String, Int]()
scores("apple") = 5
scores("apple") = 10
答案九:
scala
val names = List("alice", "bob", "charlie")
val capitalizedNames = names.map(name => name.capitalize)
答案十:
scala
val numbers = List(-1, 0, 5, -10, 20)
val positiveNumbers = numbers.filter(n => n > 0)
答案十一:
scala
val sentences = List("hello world", "hello scala")
val uniqueWords = sentences.flatMap(sentence => sentence.split(" ")).toSet.toList
答案十二:
scala
val numbers = List(1, 2, 3, 4, 5)
val product = numbers.reduce((a, b) => a * b) // or numbers.reduce(_ * _)
答案十三:
scala
val numbers = List(1, 2, 3)
val result = numbers.fold(100)((a, b) => a - b) // 100-1=99, 99-2=97, 97-3=94
答案十四:
scala
val words = List("apple", "banana", "avocado", "blueberry", "cherry")
val groupedByFirstChar = words.groupBy(word => word.charAt(0))
答案十五:
scala
val data = List(("a", 1), ("b", 2), ("a", 3))
val sumByKey = data.groupBy(_._1).mapValues(list => list.map(_._2).sum)

日期:2025年10月2日
专栏:Scala教程