集合

可變和不可變集合

語言

Scala 集合系統性地區分可變和不可變集合。可變集合可以在原地更新、縮小或擴充。這表示您可以變更、新增或移除集合的元素作為副作用。相對而言,不可變集合絕不會變更。您仍然有模擬新增、移除或更新的運算,但這些運算每次都會傳回一個新的集合,並讓舊的集合保持不變。

所有集合類別都可以在套件 scala.collection 或其子套件 mutableimmutable 中找到。用戶端代碼所需的大部分集合類別都存在於三個變體中,這些變體分別位於套件 scala.collectionscala.collection.immutablescala.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.Setcollection.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

其他類型別名為 IterableSeqIndexedSeqIteratorLazyListVectorStringBuilderRange

下圖顯示套件 scala.collection 中的所有集合。這些都是高階抽象類別或特質,通常具有可變和不可變的實作。

General collection hierarchy

下圖顯示套件 scala.collection.immutable 中的所有集合。

Immutable collection hierarchy

下圖顯示套件 scala.collection.mutable 中的所有集合。

Mutable collection hierarchy

圖例

Graph legend

集合 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 特質,它只存在於可變集合中。

以下我們將逐一檢閱這些類別。

此頁面的貢獻者