Skip to content

Commit 818de2e

Browse files
committed
Add Set.scala to stdlib
1 parent 4bc6f64 commit 818de2e

File tree

1 file changed

+269
-0
lines changed

1 file changed

+269
-0
lines changed
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala
14+
package collection
15+
16+
import scala.util.hashing.MurmurHash3
17+
import java.lang.String
18+
19+
import scala.annotation.nowarn
20+
21+
/** Base trait for set collections.
22+
*/
23+
trait Set[A]
24+
extends Iterable[A]
25+
with SetOps[A, Set, Set[A]]
26+
with Equals
27+
with IterableFactoryDefaults[A, Set] {
28+
29+
def canEqual(that: Any) = true
30+
31+
/**
32+
* Equality of sets is implemented using the lookup method [[contains]]. This method returns `true` if
33+
* - the argument `that` is a `Set`,
34+
* - the two sets have the same [[size]], and
35+
* - for every `element` this set, `other.contains(element) == true`.
36+
*
37+
* The implementation of `equals` checks the [[canEqual]] method, so subclasses of `Set` can narrow down the equality
38+
* to specific set types. The `Set` implementations in the standard library can all be compared, their `canEqual`
39+
* methods return `true`.
40+
*
41+
* Note: The `equals` method only respects the equality laws (symmetry, transitivity) if the two sets use the same
42+
* element equivalence function in their lookup operation. For example, the element equivalence operation in a
43+
* [[scala.collection.immutable.TreeSet]] is defined by its ordering. Comparing a `TreeSet` with a `HashSet` leads
44+
* to unexpected results if `ordering.equiv(e1, e2)` (used for lookup in `TreeSet`) is different from `e1 == e2`
45+
* (used for lookup in `HashSet`).
46+
*
47+
* {{{
48+
* scala> import scala.collection.immutable._
49+
* scala> val ord: Ordering[String] = _ compareToIgnoreCase _
50+
*
51+
* scala> TreeSet("A")(ord) == HashSet("a")
52+
* val res0: Boolean = false
53+
*
54+
* scala> HashSet("a") == TreeSet("A")(ord)
55+
* val res1: Boolean = true
56+
* }}}
57+
*
58+
*
59+
* @param that The set to which this set is compared
60+
* @return `true` if the two sets are equal according to the description
61+
*/
62+
override def equals(that: Any): Boolean =
63+
(this eq that.asInstanceOf[AnyRef]) || (that match {
64+
case set: Set[A @unchecked] if set.canEqual(this) =>
65+
(this.size == set.size) && {
66+
try this.subsetOf(set)
67+
catch { case _: ClassCastException => false } // PR #9565 / scala/bug#12228
68+
}
69+
case _ =>
70+
false
71+
})
72+
73+
override def hashCode(): Int = MurmurHash3.setHash(this)
74+
75+
override def iterableFactory: IterableFactory[Set] = Set
76+
77+
@nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""")
78+
override protected[this] def stringPrefix: String = "Set"
79+
80+
override def toString(): String = super[Iterable].toString() // Because `Function1` overrides `toString` too
81+
}
82+
83+
/** Base trait for set operations
84+
*
85+
* @define coll set
86+
* @define Coll `Set`
87+
*/
88+
trait SetOps[A, +CC[_], +C <: SetOps[A, CC, C]]
89+
extends IterableOps[A, CC, C]
90+
with (A => Boolean) {
91+
92+
def contains(elem: A): Boolean
93+
94+
/** Tests if some element is contained in this set.
95+
*
96+
* This method is equivalent to `contains`. It allows sets to be interpreted as predicates.
97+
* @param elem the element to test for membership.
98+
* @return `true` if `elem` is contained in this set, `false` otherwise.
99+
*/
100+
@`inline` final def apply(elem: A): Boolean = this.contains(elem)
101+
102+
/** Tests whether this set is a subset of another set.
103+
*
104+
* @param that the set to test.
105+
* @return `true` if this set is a subset of `that`, i.e. if
106+
* every element of this set is also an element of `that`.
107+
*/
108+
def subsetOf(that: Set[A]): Boolean = this.forall(that)
109+
110+
/** An iterator over all subsets of this set of the given size.
111+
* If the requested size is impossible, an empty iterator is returned.
112+
*
113+
* @param len the size of the subsets.
114+
* @return the iterator.
115+
*/
116+
def subsets(len: Int): Iterator[C] = {
117+
if (len < 0 || len > size) Iterator.empty
118+
else new SubsetsItr(this.to(IndexedSeq), len)
119+
}
120+
121+
/** An iterator over all subsets of this set.
122+
*
123+
* @return the iterator.
124+
*/
125+
def subsets(): Iterator[C] = new AbstractIterator[C] {
126+
private[this] val elms = SetOps.this.to(IndexedSeq)
127+
private[this] var len = 0
128+
private[this] var itr: Iterator[C] = Iterator.empty
129+
130+
def hasNext = len <= elms.size || itr.hasNext
131+
def next() = {
132+
if (!itr.hasNext) {
133+
if (len > elms.size) Iterator.empty.next()
134+
else {
135+
itr = new SubsetsItr(elms, len)
136+
len += 1
137+
}
138+
}
139+
140+
itr.next()
141+
}
142+
}
143+
144+
/** An Iterator including all subsets containing exactly len elements.
145+
* If the elements in 'This' type is ordered, then the subsets will also be in the same order.
146+
* ListSet(1,2,3).subsets => {{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}}
147+
*
148+
* $willForceEvaluation
149+
*
150+
*/
151+
private class SubsetsItr(elms: IndexedSeq[A], len: Int) extends AbstractIterator[C] {
152+
private[this] val idxs = Array.range(0, len+1)
153+
private[this] var _hasNext = true
154+
idxs(len) = elms.size
155+
156+
def hasNext = _hasNext
157+
@throws[NoSuchElementException]
158+
def next(): C = {
159+
if (!hasNext) Iterator.empty.next()
160+
161+
val buf = newSpecificBuilder
162+
idxs.slice(0, len) foreach (idx => buf += elms(idx))
163+
val result = buf.result()
164+
165+
var i = len - 1
166+
while (i >= 0 && idxs(i) == idxs(i+1)-1) i -= 1
167+
168+
if (i < 0) _hasNext = false
169+
else {
170+
idxs(i) += 1
171+
for (j <- (i+1) until len)
172+
idxs(j) = idxs(j-1) + 1
173+
}
174+
175+
result
176+
}
177+
}
178+
179+
/** Computes the intersection between this set and another set.
180+
*
181+
* @param that the set to intersect with.
182+
* @return a new set consisting of all elements that are both in this
183+
* set and in the given set `that`.
184+
*/
185+
def intersect(that: Set[A]): C = this.filter(that)
186+
187+
/** Alias for `intersect` */
188+
@`inline` final def & (that: Set[A]): C = intersect(that)
189+
190+
/** Computes the difference of this set and another set.
191+
*
192+
* @param that the set of elements to exclude.
193+
* @return a set containing those elements of this
194+
* set that are not also contained in the given set `that`.
195+
*/
196+
def diff(that: Set[A]): C
197+
198+
/** Alias for `diff` */
199+
@`inline` final def &~ (that: Set[A]): C = this diff that
200+
201+
@deprecated("Consider requiring an immutable Set", "2.13.0")
202+
def -- (that: IterableOnce[A]): C = {
203+
val toRemove = that.iterator.to(immutable.Set)
204+
fromSpecific(view.filterNot(toRemove))
205+
}
206+
207+
@deprecated("Consider requiring an immutable Set or fall back to Set.diff", "2.13.0")
208+
def - (elem: A): C = diff(Set(elem))
209+
210+
@deprecated("Use &- with an explicit collection argument instead of - with varargs", "2.13.0")
211+
def - (elem1: A, elem2: A, elems: A*): C = diff(elems.toSet + elem1 + elem2)
212+
213+
/** Creates a new $coll by adding all elements contained in another collection to this $coll, omitting duplicates.
214+
*
215+
* This method takes a collection of elements and adds all elements, omitting duplicates, into $coll.
216+
*
217+
* Example:
218+
* {{{
219+
* scala> val a = Set(1, 2) concat Set(2, 3)
220+
* a: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
221+
* }}}
222+
*
223+
* @param that the collection containing the elements to add.
224+
* @return a new $coll with the given elements added, omitting duplicates.
225+
*/
226+
def concat(that: collection.IterableOnce[A]): C = this match {
227+
case optimizedSet @ (_ : scala.collection.immutable.Set.Set1[A] | _: scala.collection.immutable.Set.Set2[A] | _: scala.collection.immutable.Set.Set3[A] | _: scala.collection.immutable.Set.Set4[A]) =>
228+
// StrictOptimizedSetOps optimization of concat (these Sets cannot extend StrictOptimizedSetOps because of binary-incompatible return type; cf. PR #10036)
229+
var result = optimizedSet.asInstanceOf[scala.collection.immutable.SetOps[A, scala.collection.immutable.Set, scala.collection.immutable.Set[A]]]
230+
val it = that.iterator
231+
while (it.hasNext) result = result + it.next()
232+
result.asInstanceOf[C]
233+
case _ => fromSpecific(that match {
234+
case that: collection.Iterable[A] => new View.Concat(this, that)
235+
case _ => iterator.concat(that.iterator)
236+
})
237+
}
238+
239+
@deprecated("Consider requiring an immutable Set or fall back to Set.union", "2.13.0")
240+
def + (elem: A): C = fromSpecific(new View.Appended(this, elem))
241+
242+
@deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0")
243+
def + (elem1: A, elem2: A, elems: A*): C = fromSpecific(new View.Concat(new View.Appended(new View.Appended(this, elem1), elem2), elems))
244+
245+
/** Alias for `concat` */
246+
@`inline` final def ++ (that: collection.IterableOnce[A]): C = concat(that)
247+
248+
/** Computes the union between of set and another set.
249+
*
250+
* @param that the set to form the union with.
251+
* @return a new set consisting of all elements that are in this
252+
* set or in the given set `that`.
253+
*/
254+
@`inline` final def union(that: Set[A]): C = concat(that)
255+
256+
/** Alias for `union` */
257+
@`inline` final def | (that: Set[A]): C = concat(that)
258+
}
259+
260+
/**
261+
* $factoryInfo
262+
* @define coll set
263+
* @define Coll `Set`
264+
*/
265+
@SerialVersionUID(3L)
266+
object Set extends IterableFactory.Delegate[Set](immutable.Set)
267+
268+
/** Explicit instantiation of the `Set` trait to reduce class file size in subclasses. */
269+
abstract class AbstractSet[A] extends AbstractIterable[A] with Set[A]

0 commit comments

Comments
 (0)