0%

scala集合操作

Scala 集合操作:高阶函数与函数式编程精髓

Scala 集合的强大之处不仅在于丰富的数据结构,更在于其提供的一系列高阶函数操作。这些操作允许将函数作为参数传递,实现简洁、高效的函数式编程风格,极大提升了数据处理的灵活性和可读性。本文将深入解析 Scala 集合的核心操作(映射、过滤、化简等),并展示如何利用高阶函数简化代码。

高阶函数基础

高阶函数是指可以接收函数作为参数或返回函数的函数,是 Scala 函数式编程的基础。集合操作大量依赖高阶函数,实现对元素的批量处理。

定义与示例

1
2
3
4
5
6
7
8
9
10
11
// 高阶函数:接收一个函数参数 (Int => Int) 和一个 Int,返回 Int
def process(f: Int => Int, num: Int): Int = f(num)

// 普通函数:将输入翻倍
def double(x: Int): Int = x * 2

// 调用高阶函数(传递函数作为参数)
println(process(double, 5)) // 输出:10(等价于 double(5))

// 调用时使用匿名函数(更简洁)
println(process(x => x + 3, 5)) // 输出:8

核心特点

  • 函数作为参数时,需指定其类型(如 f: Int => Int 表示接收 Int 并返回 Int 的函数)。
  • 可直接传递命名函数(如 double)或匿名函数(如 x => x + 3)。

映射操作(map 与 flatMap)

映射操作通过传入的函数转换集合中的每个元素,生成新集合。

1. map:一对一转换

map 对集合中的每个元素应用函数,返回一个新集合(元素数量与原集合相同)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val numbers = List(1, 2, 3, 4)

// 将每个元素翻倍
val doubled = numbers.map(x => x * 2)
println(doubled) // 输出:List(2, 4, 6, 8)

// 简化写法(使用 _ 代替参数)
val squared = numbers.map(_ * _)
println(squared) // 输出:List(1, 4, 9, 16)

// 字符串处理:转为大写
val words = List("scala", "java", "python")
val upperWords = words.map(_.toUpperCase)
println(upperWords) // 输出:List(SCALA, JAVA, PYTHON)

2. flatMap:一对多转换并扁平化

flatMap 先对每个元素应用函数(返回一个子集合),再将所有子集合合并为一个扁平集合(元素数量可能变化)。

1
2
3
4
5
6
7
8
9
val sentences = List("hello world", "scala is fun")

// 按空格拆分每个字符串,再合并为单列表
val words = sentences.flatMap(_.split(" "))
println(words) // 输出:List(hello, world, scala, is, fun)

// 对比 map 与 flatMap
val mapResult = sentences.map(_.split(" ")) // List(Array(hello, world), Array(scala, is, fun))
val flatMapResult = sentences.flatMap(_.split(" ")) // List(hello, world, scala, is, fun)

适用场景:拆分、展开嵌套集合(如将 List(List(1,2), List(3,4)) 转为 List(1,2,3,4))。

过滤操作(filter)

filter 根据传入的 predicate 函数(返回 Boolean)筛选元素,保留满足条件的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
val numbers = List(1, 2, 3, 4, 5, 6)

// 筛选偶数
val evenNumbers = numbers.filter(_ % 2 == 0)
println(evenNumbers) // 输出:List(2, 4, 6)

// 筛选大于3的数
val largeNumbers = numbers.filter(_ > 3)
println(largeNumbers) // 输出:List(4, 5, 6)

// 复杂条件:筛选偶数且大于2
val complexFilter = numbers.filter(x => x % 2 == 0 && x > 2)
println(complexFilter) // 输出:List(4, 6)

化简操作(reduce)

reduce 通过二元函数将集合元素逐步合并为单个值,分为 reduceLeft(从左到右)和 reduceRight(从右到左)。

1. reduceLeft(默认)

从左到右依次应用函数,将前一次的结果作为下一次计算的第一个参数。

1
2
3
4
5
6
7
8
9
val numbers = List(1, 2, 3, 4)

// 求和(1 + 2 = 3;3 + 3 = 6;6 + 4 = 10)
val sum = numbers.reduceLeft((a, b) => a + b)
println(sum) // 输出:10

// 简化写法(_ + _ 表示两个参数相加)
val product = numbers.reduce(_ * _) // 1 * 2 * 3 * 4 = 24
println(product) // 输出:24

2. reduceRight

从右到左依次应用函数,将前一次的结果作为下一次计算的第二个参数。

1
2
3
4
5
6
7
8
val numbers = List(1, 2, 3)

// 从右到左计算:1 - (2 - 3) = 1 - (-1) = 2
val result = numbers.reduceRight((a, b) => a - b)
println(result) // 输出:2

// 对比 reduceLeft:((1 - 2) - 3) = -4
println(numbers.reduceLeft(_ - _)) // 输出:-4

注意reduce 要求集合非空,否则会抛出 UnsupportedOperationException

折叠操作(fold)

foldreduce 类似,但需要显式指定初始值,支持空集合处理,分为 foldLeftfoldRight

1. foldLeft

从左到右计算,初始值作为第一次计算的第一个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val numbers = List(1, 2, 3)

// 求和(初始值 0:0 + 1 + 2 + 3 = 6)
val sum = numbers.foldLeft(0)(_ + _)
println(sum) // 输出:6

// 处理空集合(返回初始值)
val emptyList = List.empty[Int]
println(emptyList.foldLeft(0)(_ + _)) // 输出:0

// 字符串拼接
val words = List("a", "b", "c")
val concat = words.foldLeft("")(_ + _) // 初始值为空字符串
println(concat) // 输出:abc

2. foldRight

从右到左计算,初始值作为第一次计算的第二个参数。

1
2
3
4
5
val numbers = List(1, 2, 3)

// 计算:1 - (2 - (3 - 0)) = 1 - (2 - 3) = 1 - (-1) = 2
val result = numbers.foldRight(0)(_ - _)
println(result) // 输出:2

小贴士foldLeft 可用符号 /:简写,foldRight 可用 :\ 简写:

1
val sum = (0 /: numbers)(_ + _)  // 等价于 numbers.foldLeft(0)(_ + _)

扫描操作(scan)

scan 类似 fold,但会保留所有中间结果,最终返回一个包含初始值和所有中间值的集合。

1
2
3
4
5
6
7
8
9
10
11
val numbers = List(1, 2, 3)

// scanLeft:从左到右计算,保留中间结果
val scanLeftResult = numbers.scanLeft(0)(_ + _)
// 过程:0 → 0+1=1 → 1+2=3 → 3+3=6
println(scanLeftResult) // 输出:List(0, 1, 3, 6)

// scanRight:从右到左计算
val scanRightResult = numbers.scanRight(0)(_ - _)
// 过程:0 → 3-0=3 → 2-3=-1 → 1-(-1)=2
println(scanRightResult) // 输出:List(2, -1, 3, 0)

拉链操作(zip)

zip 将两个集合的元素按位置配对,形成对偶元组集合,长度与较短的集合一致。

1
2
3
4
5
6
7
8
9
10
11
val names = List("Alice", "Bob", "Charlie")
val ages = List(20, 25, 30)

// 配对为 (姓名, 年龄)
val people = names.zip(ages)
println(people) // 输出:List((Alice,20), (Bob,25), (Charlie,30))

// 长度不匹配时,以短集合为准
val scores = List(90, 85)
val nameScores = names.zip(scores)
println(nameScores) // 输出:List((Alice,90), (Bob,85))

扩展zipWithIndex 可将元素与索引配对:

1
2
val fruits = List("apple", "banana", "orange")
println(fruits.zipWithIndex) // 输出:List((apple,0), (banana,1), (orange,2))

迭代器(Iterator)

Iterator 用于遍历集合元素,适合处理大型集合(按需加载,不占用大量内存),但只能遍历一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val numbers = List(1, 2, 3, 4)
val iterator = numbers.iterator

// 第一次遍历
println("第一次遍历:")
while (iterator.hasNext) {
println(iterator.next()) // 输出:1, 2, 3, 4
}

// 第二次遍历(无元素,iterator已耗尽)
println("第二次遍历:")
while (iterator.hasNext) {
println(iterator.next()) // 无输出
}

参数类型推断与简化写法

Scala 编译器能自动推断函数参数类型,允许简化代码:

完整写法 简化写法 说明
numbers.map((x: Int) => x * 2) numbers.map(x => x * 2) 省略参数类型(编译器可推断)
numbers.map(x => x * 2) numbers.map(_ * 2) 单个参数用 _ 代替
numbers.reduce((a, b) => a + b) numbers.reduce(_ + _) 多个参数用 _ 依次代替

示例

1
2
3
4
5
6
7
8
9
10
val numbers = List(1, 2, 3, 4)

// 完整写法
numbers.filter((x: Int) => x % 2 == 0)

// 简化1:省略类型
numbers.filter(x => x % 2 == 0)

// 简化2:用 _ 代替参数
numbers.filter(_ % 2 == 0) // 结果:List(2, 4)

链式操作

集合操作可链式调用,形成声明式数据处理流水线,代码简洁且可读性高。

1
2
3
4
5
6
7
8
9
val text = "scala is a functional language"

// 链式操作:拆分 → 过滤 → 转换 → 排序
val result = text.split(" ")
.filter(_.length > 2) // 保留长度>2的单词
.map(_.capitalize) // 首字母大写
.sorted // 排序

println(result.mkString(", ")) // 输出:Functional, Language, Scala

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10