Scala 集合系統性地區分可變和不可變集合。可變集合可以在原地更新、縮小或擴充。這表示您可以變更、新增或移除集合的元素作為副作用。相對而言,不可變集合絕不會變更。您仍然有模擬新增、移除或更新的運算,但這些運算每次都會傳回一個新的集合,並讓舊的集合保持不變。
所有集合類別都可以在套件 scala.collection
或其子套件 mutable
和 immutable
中找到。用戶端代碼所需的大部分集合類別都存在於三個變體中,這些變體分別位於套件 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.immutable
中的不可變集合通常會新增用於新增或移除單一值的運算,而封裝在 scala.collection.mutable
中的可變集合通常會新增一些副作用修改運算至根介面。
根集合和不可變集合之間的另一個差異是,不可變集合的用戶端有保證,表示沒有人可以變異集合,而根集合的用戶端僅承諾不自行改變集合。即使此類集合的靜態類型未提供用於修改集合的運算,但執行時期類型仍有可能為可變集合,而其他用戶端可以改變此集合。
預設情況下,Scala 永遠會選擇不可變集合。例如,如果你只寫 Set
而沒有任何前綴或沒有從某處匯入 Set
,你會得到一個不可變集合,如果你寫 Iterable
你會得到一個不可變可迭代集合,因為這些是從 scala
套件匯入的預設繫結。若要取得可變預設版本,你需要明確寫出 collection.mutable.Set
或 collection.mutable.Iterable
。
如果你想要使用集合的可變和不可變版本,一個有用的慣例是只匯入套件 collection.mutable
。
import scala.collection.mutable
然後,一個沒有前綴的字詞,例如 Set
,仍然是指一個不可變集合,而 mutable.Set
則是指可變對應項。
集合層級中的最後一個套件是 scala.collection.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
其他類型別名為 Iterable、Seq、IndexedSeq、Iterator、LazyList、Vector、StringBuilder 和 Range。
下圖顯示套件 scala.collection
中的所有集合。這些都是高階抽象類別或特質,通常具有可變和不可變的實作。
下圖顯示套件 scala.collection.immutable
中的所有集合。
下圖顯示套件 scala.collection.mutable
中的所有集合。
圖例
集合 API 概述
最重要的集合類別顯示在上方的圖中。所有這些類別共享相當多的共通點。例如,每種類型的集合都可以透過相同的統一語法建立,寫下集合類別名稱後接其元素
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
顯示。
所有集合都支援 Iterable
所提供的 API,但會在有意義的地方使用特定類型。例如,類別 Iterable
中的 map
方法會傳回另一個 Iterable
作為結果。不過,這個結果類型會在子類別中被覆寫。例如,對 List
呼叫 map
會再傳回一個 List
,對 Set
呼叫 map
會再傳回一個 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
特質,它只存在於可變集合中。
以下我們將逐一檢閱這些類別。