过时通知
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
- 添加
+
和++
,将一个或多个元素添加到集合中,生成一个新集合。 - 删除
-
、--
,从集合中删除一个或多个元素,生成一个新集合。 - 集合运算用于并集、交集和集合差集。这些运算中的每一个都有两种形式:字母形式和符号形式。字母形式是
intersect
、union
和diff
,而符号形式是&
、|
和&~
。事实上,Set 从Traversable
继承的++
可以看作是union
或|
的另一个别名,不同之处在于++
接受Traversable
参数,而union
和|
接受集合。
Set 类中的运算
它是什么 | 它做什么 |
---|---|
测试 | |
xs 包含 x |
测试 x 是否是 xs 的元素。 |
xs(x) |
与 xs contains 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 |
xs 和 ys 的集合并集。 |
xs union ys |
与 xs | ys 相同。 |
xs &~ ys |
xs 和 ys 的集合差集。 |
xs diff ys |
与 xs &~ ys 相同。 |
可变集合还提供添加、删除或更新元素的方法,如下总结。
类 mutable.Set 中的运算
它是什么 | 它做什么 |
---|---|
添加 | |
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 |
从 xs 中删除元素 x ,如果 x 之前包含在集合中,则返回 true ,如果未包含,则返回 false 。 |
xs 保留 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
现在在同一集合上调用 -=
方法。
比较这两个交互显示了一个重要原则。您通常可以用存储在 var
中的可变集合替换存储在 val
中的不可变集合,反之亦然。至少在没有指向集合的别名引用(通过该引用可以观察到集合是就地更新还是创建了新集合)的情况下,此方法有效。
可变集合还提供 add 和 remove 作为 +=
和 -=
的变体。不同之处在于 add
和 remove
返回布尔结果,指示操作是否对集合产生了影响。
可变集合的当前默认实现使用哈希表来存储集合的元素。不可变集合的默认实现使用适应集合元素数量的表示形式。空集仅由单例对象表示。大小不超过 4 的集合由一个对象表示,该对象将所有元素存储为字段。超出该大小,不可变集合将实现为 哈希尝试。
这些表示选择的一个结果是,对于小尺寸的集合(例如最多 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 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
进行成员资格测试,或使用 +=
和 -=
进行元素添加和删除等操作都非常高效。