1. 简介

Scala 提供了一个非常丰富的集合库,位于 scala.collection 包下。
本篇文章将快速带你了解 Scala 中常用的集合类型及其使用方式。

2. Scala 集合的类型

Scala 的集合分为 可变(mutable)和不可变(immutable) 两种类型。我们先来看看它们的区别。

2.1. 可变集合(Mutable Collections)

可变集合在操作时会 原地更新或扩展,这意味着我们可以在不创建新集合的情况下添加、修改或删除元素 —— 这些操作会产生副作用。
所有可变集合类都定义在 scala.collection.mutable 包中。

2.2. 不可变集合(Immutable Collections)

不可变集合定义在 scala.collection.immutable 包中。
我们仍然可以对这些集合执行添加、删除或更新操作,但每次操作都会返回一个新的集合,原集合保持不变

3. Scala 集合体系结构概览

Screenshot-2020-05-07

Scala 的集合类继承体系以 TraversableIterable 两个特质为起点,进一步分为三个主要类别:ListSetMap

Traversable 是所有集合的基特质,提供了 foreach 方法用于遍历整个集合。

IterableTraversable 的子特质,定义了迭代器,可以逐个访问集合中的元素。
使用迭代器时,集合只能被遍历一次,因为每个元素在遍历过程中都会被处理。

接下来,我们重点介绍一些常用的不可变集合。

4. 常用的 Scala 集合类型

4.1. List

Scala 的 List 内部实现为一个 不可变的链表
它保持元素插入顺序,且允许重复元素。适用于后进先出(LIFO)的操作场景,类似栈结构。

List 实现了尾部结构共享(structural sharing),这意味着许多操作要么内存占用恒定,要么几乎不占用额外内存。

  • 头部插入(prepend)和访问 head/tail 是 O(1)
  • 其他操作如 lengthappendreverse 以及按索引查找元素都是 O(n)

示例:

val numbersList: List[Int] = List(1, 2, 3, 4)
val emptyList: List[Int] = List() // 空列表

List 由两个 case class 实现:scala.Nilscala.::,分别代表空列表和非空列表的构造。

val numbersList: List[Int] = 1 :: 2 :: 3 :: 4 :: Nil
val emptyList: List[Int] = Nil
val x :: xs = numbersList
assert(x == 1) // true
assert(xs == List(2, 3, 4)) // true

基本操作:

方法 说明
head 返回列表的第一个元素
tail 返回除第一个元素外的其余元素组成的列表
isEmpty 判断列表是否为空

示例:

val numbersList: List[Int] = 1 :: 2 :: 3 :: 4 :: Nil
assert(numbersList.head == 1) // true
assert(numbersList.tail == List(2, 3, 4)) // true
assert(numbersList.isEmpty == false) // true

常用方法:

List(1,2) ::: List(3,4) // List(1, 2, 3, 4) 拼接

List.fill(3)(100) // List(100, 100, 100) 创建相同元素的列表

List(1,2,3,4).reverse // List(4, 3, 2, 1) 反转

更多方法可参考:ScalaDoc List


4.2. Set

Scala 的 Set 表示一组 不重复的元素
默认情况下,Scala 使用的是不可变集合。

val emptySet: Set[Int] = Set() // 空 Set
val numbersSet: Set[Int] = Set(1, 2, 3, 4)

如果需要使用可变版本,需要显式导入:

val mutableSet = collection.mutable.Set(1, 2, 3)

常用方法:

方法 说明
head 返回 Set 中的一个元素
tail 返回除 head 外的其余元素
isEmpty 判断 Set 是否为空

示例:

Set(1, 2, 3, 4).head // 1
Set(1, 2, 3, 4).tail // Set(2, 3, 4)
Set(1, 2, 3, 4).isEmpty // false

更多方法参考:ScalaDoc Set


4.3. Map

Map 是一组键值对的集合,键唯一。Scala 同样提供可变和不可变两种版本,默认导入的是不可变版本。

val immutableMap = Map(1 -> "a", 2 -> "b")
val mutableMap = collection.mutable.Map(1 -> "a", 2 -> "b")

常用方法:

方法 说明
keys 返回所有键组成的集合
values 返回所有值组成的集合
isEmpty 判断 Map 是否为空

示例:

Map(1 -> "a", 2 -> "b").keys   // Set(1, 2)
Map(1 -> "a", 2 -> "b").values // Iterable("a", "b")
Map(1 -> "a", 2 -> "b").isEmpty // false

get 方法说明:

def get(key: K): Option[V]
  • 如果键存在,返回 Some(value)
  • 如果不存在,返回 None
Map(1 -> "a", 2 -> "b").get(1) // Some("a")
Map(1 -> "a", 2 -> "b").get(3) // None

更多方法参考:ScalaDoc Map


4.4. Tuple

Tuple 是一种可以将不同类型的数据组合在一起的容器。
Scala 2.x 提供了 Tuple2Tuple22,即最多支持 22 个元素的元组。

val t1 = (1, "A") // 等价于 Tuple2(1, "A")

访问方式:

val tuple3 = (1, "One", "A")
tuple3._1 // 1
tuple3._2 // One
tuple3._3 // A

或者使用模式匹配:

val (num, word, char) = (1, "One", 'A')
num  // 1
word // "One"
char // 'A'

遍历元组:

val tuple = (1,2,3,4)
tuple.productIterator.foreach(println)

输出:

1
2
3
4

⚠️ 注意:Scala 2.x 的 Tuple 并不是集合类,不继承 Iterable 特质


5. ArrayBuffer

ArrayBuffer 是 Scala 标准库提供的 可变集合,支持动态扩容、高效增删、以及通过索引访问。

虽然不如 ListMapSet 那么常用,但功能强大,值得掌握。

创建与使用:

val buffer = ArrayBuffer.empty[Int]
buffer += 1
buffer += 2
buffer += 3

验证操作结果:

buffer should have size 3
buffer(0) shouldEqual 1
buffer(1) shouldEqual 2
buffer(2) shouldEqual 3

修改元素:

buffer(1) = 5
buffer shouldEqual ArrayBuffer(1, 5, 3)

删除元素:

val buffer = ArrayBuffer(1, 2, 3)
buffer -= 2
buffer shouldEqual ArrayBuffer(1, 3)

越界访问:

val buffer = ArrayBuffer(1, 2, 3)
assertThrows[IndexOutOfBoundsException] {
  buffer(5)
}

6. 总结

本文我们简要介绍了 Scala 集合库的基本结构,对比了可变与不可变集合的差异,并详细讲解了 ListSetMapTupleArrayBuffer 的使用方式。

完整代码可参考:GitHub 项目地址


原始标题:Exception Handling in Kotlin