题目
使用Scala处理混合数据集的单词统计
信息
- 类型:问答
- 难度:⭐⭐
考点
Option类型处理,模式匹配,集合操作,函数组合
快速回答
实现步骤:
- 使用
flatMap过滤None并提取Some中的字符串 - 通过
flatMap(_.split("\\s+"))拆分单词 - 用
map(_.toLowerCase)统一大小写 - 使用
groupBy(identity)分组后mapValues(_.size)计数 - 或使用
foldLeft和Map.withDefaultValue实现高效计数
问题场景
实际开发中常需处理包含空值(None)和有效数据(Some)的混合数据集。本题要求:过滤空值→拆分字符串→统计单词频率(不区分大小写),考察对Scala核心特性的综合应用。
解决方案
def countWords(data: List[Option[String]]): Map[String, Int] = {
data
.flatMap(_.toList) // 过滤None并解包Some
.flatMap(_.split("\\s+")) // 拆分单词
.map(_.toLowerCase) // 统一小写
.groupBy(identity) // 按单词分组
.mapValues(_.size) // 计算频次
}
// 替代方案(更高性能)
def countWordsEfficient(data: List[Option[String]]): Map[String, Int] = {
data.foldLeft(Map.empty[String, Int].withDefaultValue(0)) {
(acc, option) => option match {
case Some(str) =>
str.split("\\s+").foldLeft(acc) { (map, word) =>
val lower = word.toLowerCase
map.updated(lower, map(lower) + 1)
}
case None => acc // 跳过空值
}
}
}核心原理
- Option处理:
flatMap(_.toList)等价于collect { case Some(s) => s },安全解包避免get的异常风险 - 模式匹配:在
foldLeft中匹配Some/None,符合Scala模式匹配最佳实践 - 不可变集合:
Map.updated返回新Map,保证函数式纯特性 - 正则拆分:
"\\s+"正则匹配连续空格,避免空字符串
最佳实践
- 优先使用
Option的转换方法(map/flatMap)而非直接取值 - 对大数据集采用
foldLeft方案,避免groupBy的中间集合开销 - 使用
withDefaultValue简化计数逻辑,避免getOrElse重复调用 - 单词处理统一转小写,确保统计准确性
常见错误
| 错误示例 | 后果 | 修正方案 |
|---|---|---|
data.map(_.get) | 遇到None抛出异常 | 改用flatMap(_.toList) |
split(" ") | 无法处理连续空格 | 使用split("\\s+") |
| 未处理大小写 | "Hello"和"hello"被计为不同单词 | 添加.toLowerCase |
扩展知识
- 性能优化:对于超大数据集,可用
groupMapReduce(Scala 2.13+):words.groupMapReduce(identity)(_ => 1)(_ + _) - 空值处理进阶:结合
for-comprehension提升可读性:for (opt <- data; s <- opt; word <- s.split("\\s+")) yield word.toLowerCase - 并发统计:使用
par转换为并行集合加速处理(需注意线程安全)