过时通知
Scala 集合系统地区分可变和不可变集合。可变集合可以在原处更新或扩展。这意味着您可以更改、添加或删除集合的元素作为副作用。相反,不可变集合永远不会改变。您仍然有模拟添加、删除或更新的操作,但这些操作在每种情况下都会返回一个新集合,而不会更改旧集合。
所有集合类都在包 scala.collection
或其子包 mutable
、immutable
和 generic
中。客户端代码所需的大多数集合类存在于三个变体中,它们分别位于包 scala.collection
、scala.collection.immutable
和 scala.collection.mutable
中。每个变体在可变性方面都有不同的特征。
包 scala.collection.immutable
中的集合保证对每个人都是不可变的。此类集合在创建后永远不会改变。因此,您可以依赖这样一个事实:在不同时间点重复访问相同的集合值将始终产生具有相同元素的集合。
包 scala.collection.mutable
中的集合已知有一些操作会更改集合。因此,处理可变集合意味着您需要了解哪些代码在何时更改哪些集合。
包 scala.collection
中的集合可以是可变的,也可以是不可变的。例如,collection.IndexedSeq[T] 是 collection.immutable.IndexedSeq[T] 和 collection.mutable.IndexedSeq[T] 的超类。通常,包 scala.collection
中的根集合定义与不可变集合相同的接口,而包 scala.collection.mutable
中的可变集合通常会向此不可变接口中添加一些产生副作用的修改操作。
根集合和不可变集合之间的区别在于,不可变集合的客户端可以保证没有人可以改变集合,而根集合的客户端只能保证他们自己不更改集合。即使此类集合的静态类型没有提供用于修改集合的操作,但运行时类型仍然可能是可变集合,该集合可以被其他客户端更改。
默认情况下,Scala 始终选择不可变集合。例如,如果您只编写 Set
,而没有任何前缀或没有从某个地方导入 Set
,则会得到一个不可变集合,如果您编写 Iterable
,则会得到一个不可变的 Iterable 集合,因为这些是从 scala
包导入的默认绑定。要获得可变的默认版本,您需要显式编写 collection.mutable.Set
或 collection.mutable.Iterable
。
如果您想同时使用集合的可变版本和不可变版本,一个有用的约定是仅导入包 collection.mutable
。
import scala.collection.mutable
然后,没有前缀的类似 Set
的单词仍然引用不可变集合,而 mutable.Set
引用可变对应项。
集合层次结构中的最后一个包是 collection.generic
。此包包含用于实现集合的构建模块。通常,集合类会将某些操作的实现推迟到 generic
中的类。另一方面,集合框架的用户应该仅在特殊情况下才需要参考 generic
中的类。
为了方便和向后兼容,一些重要类型在 scala
包中具有别名,因此你可以使用它们的简单名称,而无需导入。一个示例是 List
类型,它还可以作为以下内容进行访问
scala.collection.immutable.List // that's where it is defined
scala.List // via the alias in the scala package
List // because scala._
// is always automatically imported
其他别名类型包括 Traversable、Iterable、Seq、IndexedSeq、Iterator、Stream、Vector、StringBuilder 和 Range。
下图显示了包 scala.collection
中的所有集合。这些都是高级抽象类或特质,通常具有可变和不可变的实现。
下图显示了包 scala.collection.immutable
中的所有集合。
下图显示了包 scala.collection.mutable
中的所有集合。
图例
集合 API 概述
最重要的集合类显示在上面的图表中。所有这些类有很多共同点。例如,可以通过相同的统一语法创建每种类型的集合,即编写集合类名称,后跟其元素
Traversable(1, 2, 3)
Iterable("x", "y", "z")
Map("x" -> 24, "y" -> 25, "z" -> 26)
Set(Color.red, Color.green, Color.blue)
SortedSet("hello", "world")
Buffer(x, y, z)
IndexedSeq(1.0, 2.0)
LinearSeq(a, b, c)
相同的原则也适用于特定集合实现,例如
List(1, 2, 3)
HashMap("x" -> 24, "y" -> 25, "z" -> 26)
所有这些集合都以与上面所写相同的方式使用 toString
显示。
所有集合都支持 Traversable
提供的 API,但在有意义的地方对类型进行专门化。例如,Traversable
类中的 map
方法将另一个 Traversable
作为其结果返回。但此结果类型在子类中被覆盖。例如,对 List
调用 map
会再次产生一个 List
,对 Set
调用它会再次产生一个 Set
,依此类推。
scala> List(1, 2, 3) map (_ + 1)
res0: List[Int] = List(2, 3, 4)
scala> Set(1, 2, 3) map (_ * 2)
res0: Set[Int] = Set(2, 4, 6)
这种在集合库中无处不在的行为称为统一返回类型原则。
集合层次结构中的大多数类都存在三种变体:根、可变和不可变。唯一的例外是 Buffer 特征,它只存在于可变集合中。
接下来,我们将逐一查看这些类。