集合(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 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 xsys 的集合交集。
xs intersect ys xs & ys 相同。
xs | ys xsys 的集合并集。
xs union ys xs | ys 相同。
xs &~ ys xsys 的集合差集。
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))。如果布尔参数 btrue,则将 x 添加到 xs,否则从 xs 中移除 x
克隆  
xs.clone 一个新的可变集合,其元素与 xs 相同。

与不可变集合一样,可变集合也提供 +++ 操作来添加元素,以及 --- 操作来移除元素。但这些操作对于可变集合而言使用较少,因为它们涉及复制集合。作为一种更高效的替代方案,可变集合提供了更新方法 +=-=。操作 s += elemelem 作为副作用添加到集合 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 作为 +=-= 的变体。不同之处在于 addremove 返回布尔结果,指示操作是否对集合产生了影响。

可变集合的当前默认实现使用哈希表来存储集合的元素。不可变集合的默认实现使用适应集合元素数量的表示形式。空集仅由单例对象表示。大小不超过 4 的集合由一个对象表示,该对象将所有元素存储为字段。超出该大小,不可变集合将实现为 哈希尝试

这些表示选择的一个结果是,对于小尺寸的集合(例如最多 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 进行成员资格测试,或使用 +=-= 进行元素添加和删除等操作都非常高效。

此页面的贡献者