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