集合 (Scala 2.8 - 2.12)

集合

語言

Set 是不包含重複元素的 Iterable。集合上的操作總結在以下表格中,針對一般集合,以及在後面的表格中,針對可變集合。它們屬於以下類別

  • 測試 containsapplysubsetOfcontains 方法會詢問一個集合是否包含給定的元素。集合的 apply 方法與 contains 相同,因此 set(elem)set contains elem 相同。這表示集合也可以用作測試函數,為其包含的元素傳回 true。

例如

scala> val fruit = Set("apple", "orange", "peach", "banana")
fruit: scala.collection.immutable.Set[java.lang.String] = Set(apple, orange, peach, banana)
scala> fruit("peach")
res0: Boolean = true
scala> fruit("potato")
res1: Boolean = false
  • 加法 +++,會將一個或多個元素加入集合,產生一個新的集合。
  • 移除 ---,會從集合中移除一個或多個元素,產生一個新的集合。
  • 集合運算,用於聯集、交集和集合差集。這些運算各有兩種形式:字母和符號。字母版本為 intersectuniondiff,而符號版本為 &|&~。事實上,Set 從 Traversable 繼承的 ++ 可以視為 union| 的另一個別名,差別在於 ++ 會使用 Traversable 參數,而 union| 則會使用集合。

Set 類別中的運算

它是什麼 用途
測試  
xs 包含 x 測試 x 是否為 xs 的元素。
xs(x) xs 包含 x 相同。
xs 為 ys 的子集 測試 xs 是否為 ys 的子集。
新增  
xs + x 包含 xs 的所有元素以及 x 的集合。
xs + (x, y, z) 包含 xs 的所有元素以及給定的其他元素的集合。
xs ++ ys 包含 xs 的所有元素以及 ys 的所有元素的集合。
移除  
xs - x 包含 xs 的所有元素,但不包含 x 的集合。
xs - (x, y, z) 包含 xs 的所有元素,但不包含給定元素的集合。
xs -- ys 包含 xs 的所有元素,但不包含 ys 的元素的集合。
xs.empty xs 相同類別的空集合。
二元運算  
xs & ys xs 和 ys 的集合交集。
xs intersect ys xs & ys 相同。
xs | ys xsys 的集合聯集。
xs 聯集 ys xs | ys 相同。
xs &~ ys xsys 的集合差集。
xs 差集 ys xs &~ ys 相同。

可變集合提供額外的函式來新增、移除或更新元素,如下所述。

可變集合類別中的運算

它是什麼 用途
新增  
xs += x 將元素 x 新增到集合 xs 中,並將 xs 本身作為副作用傳回。
xs += (x, y, z) 將指定的元素新增到集合 xs 中,並將 xs 本身作為副作用傳回。
xs ++= ys ys 中的所有元素新增到集合 xs 中,並將 xs 本身作為副作用傳回。
xs add x 將元素 x 新增到 xs 中,如果 x 之前未包含在集合中,則傳回 true,如果已包含,則傳回 false
移除  
xs -= x 從集合 xs 中移除元素 x,並將 xs 本身作為副作用傳回。
xs -= (x, y, z) 從集合 xs 中移除指定的元素,並將 xs 本身作為副作用傳回。
xs --= ys 從集合 xs 中移除 ys 中的所有元素,並將 xs 本身作為副作用傳回。
xs remove x 移除元素 xxs 並回傳 true 如果 x 之前包含在集合中,false 如果沒有。
xs 保留 p 只保留 xs 中滿足謂詞 p 的元素。
xs.clear() 移除 xs 中的所有元素。
更新  
xs(x) = b (或寫成 xs.update(x, b))。如果布林參數 btrue,將 x 新增到 xs,否則從 xs 中移除 x
複製  
xs.clone 一個新的可變集合,與 xs 具有相同的元素。

就像一個不可變集合,可變集合提供 +++ 運算,用於新增元素,以及 --- 運算,用於移除元素。但這些較少用於可變集合,因為它們會複製集合。可變集合提供更新方法 +=-=,作為更有效率的替代方案。運算 s += elem 會將 elem 新增到集合 s 作為副作用,並回傳已變異的集合作為結果。同樣地,s -= elem 會從集合中移除 elem,並回傳已變異的集合作為結果。除了 +=-= 之外,還有大量運算 ++=--=,用於新增或移除可遍歷物件或迭代器中的所有元素。

方法名稱 +=-= 的選擇表示非常相似的程式碼可以用於可變或不可變集合。首先考慮以下使用不可變集合 s 的 REPL 對話

scala> var s = Set(1, 2, 3)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> s += 4
scala> s -= 2
scala> s
res2: scala.collection.immutable.Set[Int] = Set(1, 3, 4)

我們在型別為 immutable.Setvar 上使用 +=-=。陳述式 s += 4s = s + 4 的簡寫。因此,這會在集合 s 上呼叫加法方法 +,然後將結果指派回 s 變數。現在考慮與可變集合的類比互動。

scala> val s = collection.mutable.Set(1, 2, 3)
s: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> s += 4
res3: s.type = Set(1, 4, 2, 3)
scala> s -= 2
res4: s.type = Set(1, 4, 3)

最終效果與先前的互動非常類似;我們從 Set(1, 2, 3) 開始,並以 Set(1, 3, 4) 結束。然而,儘管這些陳述看起來與之前相同,但它們會執行不同的操作。 s += 4 現在會在可變集合值 s 上呼叫 += 方法,並就地變更集合。同樣地, s -= 2 現在會在同一個集合上呼叫 -= 方法。

比較這兩個互動會顯示一個重要的原則。您通常可以將儲存在 val 中的可變集合替換為儲存在 var 中的不可變集合,反之亦然。至少在沒有透過別名參照集合(透過別名參照集合可以觀察到集合是否就地更新或是否已建立新的集合)的情況下,這個方法有效。

可變集合也提供 +=-= 的變體,作為 add 和 remove。不同之處在於 addremove 會傳回布林結果,表示操作是否對集合產生影響。

可變集合的目前預設實作會使用雜湊表來儲存集合的元素。不可變集合的預設實作會使用一種適應集合元素數量的表示法。空集合僅由一個單例物件表示。大小最多為四的集合由一個物件表示,該物件將所有元素儲存為欄位。超過該大小後,不可變集合會實作為 雜湊嘗試

這些表示法選項的結果是,對於小尺寸的集合(例如最多 4 個),不可變集合通常比可變集合更緊湊且更有效率。因此,如果您預期集合的大小會很小,請嘗試使其不可變。

集合的兩個子集是 SortedSetBitSet

已排序的集合

一個 SortedSet 是以給定順序(可以在建立集合時自由選擇)產生其元素(使用 iteratorforeach)的集合。一個 SortedSet 的預設表示法是一個已排序的二元樹,它維持不變式,即一個節點的左子樹中的所有元素都比右子樹中的所有元素小。這樣,一個簡單的順序遍歷可以按遞增順序傳回所有樹元素。Scala 的類別 immutable.TreeSet 使用一個紅黑樹實作來維持這個排序不變式,同時保持樹平衡– 表示從樹的根到一個葉子的所有路徑的長度只相差最多一個元素。

若要建立一個空的 TreeSet,您可以先指定想要的順序

scala> val myOrdering = Ordering.fromLessThan[String](_ > _)
myOrdering: scala.math.Ordering[String] = ...

然後,使用以下方式建立一個具有該順序的空樹集合

scala> TreeSet.empty(myOrdering)
res1: scala.collection.immutable.TreeSet[String] = TreeSet()

或者您可以省略排序參數,但給定一個元素類型或一個空集合。在這種情況下,將使用元素類型上的預設排序。

scala> TreeSet.empty[String]
res2: scala.collection.immutable.TreeSet[String] = TreeSet()

如果您從一個樹集合建立新的集合(例如透過串接或篩選),它們將保持與原始集合相同的順序。例如,

scala> res2 + ("one", "two", "three", "four")
res3: scala.collection.immutable.TreeSet[String] = TreeSet(four, one, three, two)

已排序的集合也支援元素範圍。例如,range 方法會傳回從開始元素到結束元素(不含結束元素)的所有元素。或者,from 方法會傳回集合排序中大於或等於開始元素的所有元素。這兩個方法呼叫的結果都是已排序的集合。範例

scala> res3 range ("one", "two")
res4: scala.collection.immutable.TreeSet[String] = TreeSet(one, three)
scala> res3 from "three"
res5: scala.collection.immutable.TreeSet[String] = TreeSet(three, two)

位元集

位元集是非負整數元素的集合,實作於一個或多個已封裝位元的字元中。 BitSet 的內部表示法使用 Long 陣列。第一個 Long 涵蓋元素 0 到 63,第二個涵蓋 64 到 127,依此類推(0 到 127 範圍中元素的不變位元集會最佳化陣列,並將位元直接儲存在一個或兩個 Long 欄位中。)對於每個 Long,其 64 個位元中的每個位元如果對應的元素包含在集合中,就會設定為 1,否則會取消設定。因此,位元集的大小取決於儲存在其中的最大整數。如果 N 是該最大整數,則集合的大小為 N/64 Long 字元,或 N/8 位元組,加上少數額外的狀態資訊位元組。

因此,如果位元集包含許多小元素,則位元集會比其他集合更精簡。位元集的另一個優點是,使用 contains 進行成員測試,或使用 +=-= 進行元素新增和移除等作業都非常有效率。

此頁面的貢獻者