Set 是 Iterable,不包含重复元素。以下表格总结了对一般集合、不可变集合和可变集合的操作。它们分为以下类别
- 测试
contains、apply、subsetOf。contains方法询问集合是否包含给定元素。集合的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
- 添加
incl和concat(或分别为+和++),它们将一个或多个元素添加到集合中,生成一个新的集合。 - 移除
excl和removedAll(或分别为-和--),它们从集合中移除一个或多个元素,生成一个新的集合。 - 集合操作 用于并集、交集和集合差。每个操作都存在两种形式:字母形式和符号形式。字母形式为
intersect、union和diff,而符号形式为&、|和&~。事实上,Set从Iterable继承的++可以被视为union或|的另一个别名,除了++接受IterableOnce参数,而union和|接受集合。
类 Set 中的操作
| 什么是集合 | 集合的功能 |
|---|---|
| 测试 | |
xs 包含 x |
测试 x 是否是 xs 的元素。 |
xs(x) |
与 xs contains x 相同。 |
xs 是 ys 的子集 |
测试 xs 是否是 ys 的子集。 |
| 添加 | |
xs 连接 ys或 xs ++ ys |
包含 xs 和 ys 所有元素的集合。 |
| 移除 | |
xs.empty |
与 xs 同类的一个空集合。 |
| 二元运算 | |
xs 交集 ys或 xs & ys |
xs 和 ys 的交集。 |
xs 并集 ys或 xs | ys |
xs 和 ys 的并集。 |
xs 差集 ys或 xs &~ ys |
xs 和 ys 的差集。 |
不可变集合提供方法通过返回新的 Set 来添加或删除元素,总结如下。
immutable.Set 类中的操作
| 什么是集合 | 集合的功能 |
|---|---|
| 添加 | |
xs 包含 x或 xs + x |
包含 xs 和 x 所有元素的集合。 |
| 移除 | |
xs 排除 x或 xs - x |
包含 xs 所有元素,除了 x 的集合。 |
xs 删除所有 ys或 xs -- ys |
包含 xs 所有元素,除了 ys 元素的集合。 |
可变集合除了提供添加、删除或更新元素的方法外,还总结如下。
mutable.Set 类中的操作
| 什么是集合 | 集合的功能 |
|---|---|
| 添加 | |
xs addOne x或 xs += x |
将元素 x 添加到集合 xs 中,并返回 xs 本身。 |
xs addAll ys或 xs ++= ys |
将集合 ys 中的所有元素添加到集合 xs 中,并返回 xs 本身。 |
xs add x |
将元素 x 添加到 xs 中,如果 x 之前不在集合中,则返回 true,否则返回 false。 |
| 移除 | |
xs subtractOne x或 xs -= x |
从集合 xs 中删除元素 x,并返回 xs 本身。 |
xs subtractAll ys或 xs --= ys |
从集合 xs 中删除集合 ys 中的所有元素,并返回 xs 本身。 |
xs remove x |
从 xs 中删除元素 x,如果 x 之前在集合中,则返回 true,否则返回 false。 |
xs filterInPlace p |
仅保留 xs 中满足谓词 p 的元素。 |
xs.clear() |
从 xs 中删除所有元素。 |
| 更新 | |
xs(x) = b |
(或者,写出来,xs.update(x, b))。如果布尔参数 b 为 true,则将 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.Set 的 var 上使用了 += 和 -=。语句如 s += 4 是 s = 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 作为 += 和 -= 的变体。不同之处在于 add 和 remove 返回一个布尔结果,指示操作是否对集合产生了影响。
可变集合的当前默认实现使用哈希表来存储集合的元素。不可变集合的默认实现使用一种适应集合元素数量的表示形式。空集合由一个单例对象表示。大小不超过四个的集合由一个存储所有元素作为字段的单一对象表示。超过该大小,不可变集合被实现为 压缩哈希数组映射前缀树。
这些表示选择的结果是,对于小尺寸的集合(例如不超过 4),不可变集合通常比可变集合更紧凑,效率也更高。因此,如果您预计集合的大小很小,请尝试将其设为不可变。
集合的两个子类型是 SortedSet 和 BitSet。
排序集
一个 SortedSet 是一个集合,它以给定的顺序(可以在创建集合时自由选择)生成其元素(使用 iterator 或 foreach)。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 rangeFrom "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 进行成员资格测试,或使用 += 和 -= 进行元素添加和删除的操作都非常高效。