Scala Set(集合)(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在编程世界中,数据的组织与操作是开发者的核心任务之一。Scala 作为一种兼具面向对象与函数式编程特性的语言,其提供的集合(Collection)框架设计精良且功能丰富。其中,Scala Set(集合)因其无序性不可重复性高效集合运算的特性,在数据去重、关系判断等场景中扮演着重要角色。无论是处理用户行为日志、管理权限列表,还是执行数学集合运算,Scala Set都能提供简洁高效的解决方案。本文将从基础概念到高级应用,逐步解析这一集合类型的使用技巧与核心原理。


什么是 Scala Set?

基本定义与特性

Scala Set 是一种无序且不重复的集合结构。它类似于数学中的集合概念,其核心特性包括:

  1. 无序性:元素的存储和访问顺序不固定,无法通过索引直接访问元素;
  2. 不可重复性:集合中的每个元素唯一,添加重复元素时仅保留一份;
  3. 高效集合运算:支持快速的交集、并集、差集等操作。

形象比喻:可以将 Set 想象成一个图书馆的书架,每本书(元素)只能存在一份,且书的排列顺序无关紧要。当你需要快速判断某本书是否在架上时,无需逐本查找,而是通过某种高效的检索机制直接确认。

Scala Set 的类型分层

Scala 的 Set 类型分为两大分支:

  • 不可变集合(Immutable Set):如 scala.collection.immutable.Set,所有操作均返回新实例,适用于函数式编程场景;
  • 可变集合(Mutable Set):如 scala.collection.mutable.Set,允许直接修改原集合内容,适合需要频繁更新的场景。

关键区别
| 特性 | 不可变 Set | 可变 Set |
|---------------|---------------------|---------------------|
| 修改方式 | 返回新实例 | 直接修改原集合 |
| 线程安全性 | 内置线程安全 | 需手动加锁 |
| 性能特点 | 适用于读多写少场景 | 适用于频繁增删场景 |


如何创建 Scala Set?

不同方式创建 Set

1. 使用工厂方法(Factory Method)

通过 Set 对象的 apply 方法直接创建:

val mySet = Set(1, 2, 3, 3) // 输出:Set(1, 2, 3)  

此方法会自动去除重复元素,并返回一个不可变 Set

2. 显式指定类型参数

若元素类型不明确,可通过类型参数显式声明:

val stringSet: Set[String] = Set("apple", "banana", "apple")  
// 输出:Set(apple, banana)  

3. 可变 Set 的创建

需要导入 scala.collection.mutable.Set 并使用 new 关键字:

import scala.collection.mutable.Set  
val mutableSet = Set[Int]()  
mutableSet += 10 // 直接修改集合内容  

Scala Set 的核心操作方法

基础操作:添加、删除与查询

添加元素

对于不可变 Set,添加元素会返回新实例:

val original = Set(1, 2)  
val newSet = original + 3 // 新 Set 包含 1, 2, 3  

而可变 Set 可直接修改:

mutableSet += 5  

删除元素

删除操作同样遵循不可变与可变的差异:

val filtered = original - 2 // 不可变 Set 删除元素  
mutableSet -= 10 // 可变 Set 删除元素  

元素查询

使用 contains 方法判断元素是否存在:

original.contains(2) // 返回 true  

集合运算:交集、并集与差集

Scala Set 的核心优势在于其高效的集合运算,这些操作均基于数学集合的原理实现:

1. 交集(Intersection)

val setA = Set(1, 2, 3)  
val setB = Set(3, 4, 5)  
val intersection = setA & setB // 输出 Set(3)  

2. 并集(Union)

val union = setA ++ setB // 输出 Set(1, 2, 3, 4, 5)  

3. 差集(Difference)

val difference = setA - setB // 输出 Set(1, 2)  

运算效率:由于 Set 的底层实现通常基于哈希表或二叉树结构,上述运算的时间复杂度接近 O(1) 或 O(n),远优于列表(List)的线性时间复杂度。


不可变 Set 与可变 Set 的选择策略

场景分析与代码示例

1. 不可变 Set 的适用场景

  • 函数式编程:避免副作用,确保数据不可变;
  • 并发环境:天然线程安全,无需额外同步机制。

示例:统计用户访问路径的唯一 URL:

def trackUserPaths(paths: List[String]): Set[String] = {  
  paths.foldLeft(Set.empty[String]) { (acc, path) =>  
    acc + path // 每次迭代返回新 Set  
  }  
}  

2. 可变 Set 的适用场景

  • 频繁增删操作:修改原集合比生成新实例更高效;
  • 单线程快速迭代:例如临时缓存的管理。

示例:实时过滤重复日志条目:

import scala.collection.mutable.Set  
val logEntries = Set[String]()  
for (line <- logFileLines) {  
  if (!logEntries.contains(line)) {  
    logEntries += line // 直接添加到可变 Set  
    process(line)  
  }  
}  

高级技巧与常见问题

1. Set 与 List 的对比

虽然 SetList 均为集合类型,但核心区别在于:

  • 顺序性List 有序且可重复,Set 无序且唯一;
  • 操作效率Set 的成员查询(contains)通常为 O(1),而 List 需 O(n)。

使用建议:当需要快速判断元素是否存在时,优先选择 Set

2. 空 Set 的创建与判空

  • 空 Set:通过 Set.emptySet() 创建;
  • 判空:使用 isEmpty 方法,避免直接比较 ==
val emptySet = Set.empty[Int]  
if (mySet.isEmpty) { /* 处理空集合逻辑 */ }  

3. 遍历与转换

遍历 Set 可使用 foreachfor 循环:

mySet.foreach(println) // 逐个打印元素  
for (element <- mySet) {  
  process(element)  
}  

若需将 Set 转换为其他集合类型:

val listFromSet = mySet.toList // 转换为 List  
val arrayFromSet = mySet.toArray // 转换为 Array  

实战案例:去重与统计

案例背景

假设我们需从用户行为日志中统计所有唯一访问的城市,并计算不同城市之间的交集

原始数据

val userLogs = List(  
  "User1: Beijing",  
  "User2: Shanghai",  
  "User3: Beijing",  
  "User4: Tokyo"  
)  

实现步骤

  1. 提取城市名称并去重;
  2. 计算不同用户群体的城市交集。
// 步骤1:提取唯一城市  
val cities = userLogs.map(_.split(": ")(1)).toSet // 转换为 Set 去重  

// 步骤2:假设另一组日志  
val userLogs2 = List("User5: Tokyo", "User6: Paris")  
val cities2 = userLogs2.map(_.split(": ")(1)).toSet  

// 计算交集  
val commonCities = cities & cities2 // 输出 Set(Tokyo)  

总结

通过本文的讲解,我们掌握了 Scala Set 的核心概念、操作方法及实际应用场景。其无序性、不可重复性与高效集合运算的特性,使其成为处理数据去重、关系判断等任务的理想工具。无论是函数式编程中的不可变集合,还是需要频繁修改的可变集合,开发者均能通过合理选择实现代码的高效与优雅。

在后续学习中,可进一步探索 SortedSet(有序集合)、BitSet(位集合)等子类型,以及集合的泛型扩展与自定义逻辑。通过将 Set 与 Scala 的函数式编程范式结合,开发者能构建出简洁且高性能的解决方案。

希望本文能为你的 Scala 学习之路提供一份清晰的指南!

最新发布