Skip to content

Commit 3f2d39e

Browse files
committed
Add mutable/immutable Set to stdlib
1 parent 1a7d1a5 commit 3f2d39e

File tree

2 files changed

+520
-0
lines changed

2 files changed

+520
-0
lines changed
Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
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+
package immutable
16+
17+
import scala.collection.immutable.Set.Set4
18+
import scala.collection.mutable.{Builder, ReusableBuilder}
19+
20+
/** Base trait for immutable set collections */
21+
trait Set[A] extends Iterable[A]
22+
with collection.Set[A]
23+
with SetOps[A, Set, Set[A]]
24+
with IterableFactoryDefaults[A, Set] {
25+
override def iterableFactory: IterableFactory[Set] = Set
26+
}
27+
28+
/** Base trait for immutable set operations
29+
*
30+
* @define coll immutable set
31+
* @define Coll `immutable.Set`
32+
*/
33+
trait SetOps[A, +CC[X], +C <: SetOps[A, CC, C]]
34+
extends collection.SetOps[A, CC, C] {
35+
36+
/** Creates a new set with an additional element, unless the element is
37+
* already present.
38+
*
39+
* @param elem the element to be added
40+
* @return a new set that contains all elements of this set and that also
41+
* contains `elem`.
42+
*/
43+
def incl(elem: A): C
44+
45+
/** Alias for `incl` */
46+
override final def + (elem: A): C = incl(elem) // like in collection.Set but not deprecated
47+
48+
/** Creates a new set with a given element removed from this set.
49+
*
50+
* @param elem the element to be removed
51+
* @return a new set that contains all elements of this set but that does not
52+
* contain `elem`.
53+
*/
54+
def excl(elem: A): C
55+
56+
/** Alias for `excl` */
57+
@`inline` final override def - (elem: A): C = excl(elem)
58+
59+
def diff(that: collection.Set[A]): C =
60+
foldLeft(empty)((result, elem) => if (that contains elem) result else result + elem)
61+
62+
/** Creates a new $coll from this $coll by removing all elements of another
63+
* collection.
64+
*
65+
* @param that the collection containing the elements to remove.
66+
* @return a new $coll with the given elements removed, omitting duplicates.
67+
*/
68+
def removedAll(that: IterableOnce[A]): C = that.iterator.foldLeft[C](coll)(_ - _)
69+
70+
/** Alias for removedAll */
71+
override final def -- (that: IterableOnce[A]): C = removedAll(that)
72+
}
73+
74+
trait StrictOptimizedSetOps[A, +CC[X], +C <: SetOps[A, CC, C]]
75+
extends SetOps[A, CC, C]
76+
with collection.StrictOptimizedSetOps[A, CC, C]
77+
with StrictOptimizedIterableOps[A, CC, C] {
78+
79+
override def concat(that: collection.IterableOnce[A]): C = {
80+
var result: C = coll
81+
val it = that.iterator
82+
while (it.hasNext) result = result + it.next()
83+
result
84+
}
85+
}
86+
87+
/**
88+
* $factoryInfo
89+
* @define coll immutable set
90+
* @define Coll `immutable.Set`
91+
*/
92+
@SerialVersionUID(3L)
93+
object Set extends IterableFactory[Set] {
94+
95+
def empty[A]: Set[A] = EmptySet.asInstanceOf[Set[A]]
96+
97+
def from[E](it: collection.IterableOnce[E]): Set[E] =
98+
it match {
99+
// We want `SortedSet` (and subclasses, such as `BitSet`) to
100+
// rebuild themselves to avoid element type widening issues
101+
case _: SortedSet[E] => (newBuilder[E] ++= it).result()
102+
case _ if it.knownSize == 0 => empty[E]
103+
case s: Set[E] => s
104+
case _ => (newBuilder[E] ++= it).result()
105+
}
106+
107+
def newBuilder[A]: Builder[A, Set[A]] = new SetBuilderImpl[A]
108+
109+
/** An optimized representation for immutable empty sets */
110+
@SerialVersionUID(3L)
111+
private object EmptySet extends AbstractSet[Any] with Serializable {
112+
override def size: Int = 0
113+
override def isEmpty = true
114+
override def knownSize: Int = size
115+
override def filter(pred: Any => Boolean): Set[Any] = this
116+
override def filterNot(pred: Any => Boolean): Set[Any] = this
117+
override def removedAll(that: IterableOnce[Any]): Set[Any] = this
118+
override def diff(that: collection.Set[Any]): Set[Any] = this
119+
override def subsetOf(that: collection.Set[Any]): Boolean = true
120+
override def intersect(that: collection.Set[Any]): Set[Any] = this
121+
override def view: View[Any] = View.empty
122+
def contains(elem: Any): Boolean = false
123+
def incl(elem: Any): Set[Any] = new Set1(elem)
124+
def excl(elem: Any): Set[Any] = this
125+
def iterator: Iterator[Any] = Iterator.empty
126+
override def foreach[U](f: Any => U): Unit = ()
127+
}
128+
private[collection] def emptyInstance: Set[Any] = EmptySet
129+
130+
@SerialVersionUID(3L)
131+
private abstract class SetNIterator[A](n: Int) extends AbstractIterator[A] with Serializable {
132+
private[this] var current = 0
133+
private[this] var remainder = n
134+
override def knownSize: Int = remainder
135+
def hasNext = remainder > 0
136+
def apply(i: Int): A
137+
def next(): A =
138+
if (hasNext) {
139+
val r = apply(current)
140+
current += 1
141+
remainder -= 1
142+
r
143+
} else Iterator.empty.next()
144+
145+
override def drop(n: Int): Iterator[A] = {
146+
if (n > 0) {
147+
current += n
148+
remainder = Math.max(0, remainder - n)
149+
}
150+
this
151+
}
152+
}
153+
154+
/** An optimized representation for immutable sets of size 1 */
155+
@SerialVersionUID(3L)
156+
final class Set1[A] private[collection] (elem1: A) extends AbstractSet[A] with StrictOptimizedIterableOps[A, Set, Set[A]] with Serializable {
157+
override def size: Int = 1
158+
override def isEmpty = false
159+
override def knownSize: Int = size
160+
def contains(elem: A): Boolean = elem == elem1
161+
def incl(elem: A): Set[A] =
162+
if (contains(elem)) this
163+
else new Set2(elem1, elem)
164+
def excl(elem: A): Set[A] =
165+
if (elem == elem1) Set.empty
166+
else this
167+
def iterator: Iterator[A] = Iterator.single(elem1)
168+
override def foreach[U](f: A => U): Unit = f(elem1)
169+
override def exists(p: A => Boolean): Boolean = p(elem1)
170+
override def forall(p: A => Boolean): Boolean = p(elem1)
171+
override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Set[A] =
172+
if (pred(elem1) != isFlipped) this else Set.empty
173+
174+
override def find(p: A => Boolean): Option[A] =
175+
if (p(elem1)) Some(elem1)
176+
else None
177+
override def head: A = elem1
178+
override def tail: Set[A] = Set.empty
179+
}
180+
181+
/** An optimized representation for immutable sets of size 2 */
182+
@SerialVersionUID(3L)
183+
final class Set2[A] private[collection] (elem1: A, elem2: A) extends AbstractSet[A] with StrictOptimizedIterableOps[A, Set, Set[A]] with Serializable {
184+
override def size: Int = 2
185+
override def isEmpty = false
186+
override def knownSize: Int = size
187+
def contains(elem: A): Boolean = elem == elem1 || elem == elem2
188+
def incl(elem: A): Set[A] =
189+
if (contains(elem)) this
190+
else new Set3(elem1, elem2, elem)
191+
def excl(elem: A): Set[A] =
192+
if (elem == elem1) new Set1(elem2)
193+
else if (elem == elem2) new Set1(elem1)
194+
else this
195+
def iterator: Iterator[A] = new SetNIterator[A](size) {
196+
def apply(i: Int) = getElem(i)
197+
}
198+
private def getElem(i: Int) = i match { case 0 => elem1 case 1 => elem2 }
199+
200+
override def foreach[U](f: A => U): Unit = {
201+
f(elem1); f(elem2)
202+
}
203+
override def exists(p: A => Boolean): Boolean = {
204+
p(elem1) || p(elem2)
205+
}
206+
override def forall(p: A => Boolean): Boolean = {
207+
p(elem1) && p(elem2)
208+
}
209+
override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Set[A] = {
210+
var r1: A = null.asInstanceOf[A]
211+
var n = 0
212+
if (pred(elem1) != isFlipped) { r1 = elem1; n += 1}
213+
if (pred(elem2) != isFlipped) { if (n == 0) r1 = elem2; n += 1}
214+
215+
n match {
216+
case 0 => Set.empty
217+
case 1 => new Set1(r1)
218+
case 2 => this
219+
}
220+
}
221+
override def find(p: A => Boolean): Option[A] = {
222+
if (p(elem1)) Some(elem1)
223+
else if (p(elem2)) Some(elem2)
224+
else None
225+
}
226+
override def head: A = elem1
227+
override def tail: Set[A] = new Set1(elem2)
228+
}
229+
230+
/** An optimized representation for immutable sets of size 3 */
231+
@SerialVersionUID(3L)
232+
final class Set3[A] private[collection] (elem1: A, elem2: A, elem3: A) extends AbstractSet[A] with StrictOptimizedIterableOps[A, Set, Set[A]] with Serializable {
233+
override def size: Int = 3
234+
override def isEmpty = false
235+
override def knownSize: Int = size
236+
def contains(elem: A): Boolean =
237+
elem == elem1 || elem == elem2 || elem == elem3
238+
def incl(elem: A): Set[A] =
239+
if (contains(elem)) this
240+
else new Set4(elem1, elem2, elem3, elem)
241+
def excl(elem: A): Set[A] =
242+
if (elem == elem1) new Set2(elem2, elem3)
243+
else if (elem == elem2) new Set2(elem1, elem3)
244+
else if (elem == elem3) new Set2(elem1, elem2)
245+
else this
246+
def iterator: Iterator[A] = new SetNIterator[A](size) {
247+
def apply(i: Int) = getElem(i)
248+
}
249+
private def getElem(i: Int) = i match { case 0 => elem1 case 1 => elem2 case 2 => elem3 }
250+
251+
override def foreach[U](f: A => U): Unit = {
252+
f(elem1); f(elem2); f(elem3)
253+
}
254+
override def exists(p: A => Boolean): Boolean = {
255+
p(elem1) || p(elem2) || p(elem3)
256+
}
257+
override def forall(p: A => Boolean): Boolean = {
258+
p(elem1) && p(elem2) && p(elem3)
259+
}
260+
override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Set[A] = {
261+
var r1, r2: A = null.asInstanceOf[A]
262+
var n = 0
263+
if (pred(elem1) != isFlipped) { r1 = elem1; n += 1}
264+
if (pred(elem2) != isFlipped) { if (n == 0) r1 = elem2 else r2 = elem2; n += 1}
265+
if (pred(elem3) != isFlipped) { if (n == 0) r1 = elem3 else if (n == 1) r2 = elem3; n += 1}
266+
267+
n match {
268+
case 0 => Set.empty
269+
case 1 => new Set1(r1)
270+
case 2 => new Set2(r1, r2)
271+
case 3 => this
272+
}
273+
}
274+
override def find(p: A => Boolean): Option[A] = {
275+
if (p(elem1)) Some(elem1)
276+
else if (p(elem2)) Some(elem2)
277+
else if (p(elem3)) Some(elem3)
278+
else None
279+
}
280+
override def head: A = elem1
281+
override def tail: Set[A] = new Set2(elem2, elem3)
282+
}
283+
284+
/** An optimized representation for immutable sets of size 4 */
285+
@SerialVersionUID(3L)
286+
final class Set4[A] private[collection] (elem1: A, elem2: A, elem3: A, elem4: A) extends AbstractSet[A] with StrictOptimizedIterableOps[A, Set, Set[A]] with Serializable {
287+
override def size: Int = 4
288+
override def isEmpty = false
289+
override def knownSize: Int = size
290+
def contains(elem: A): Boolean =
291+
elem == elem1 || elem == elem2 || elem == elem3 || elem == elem4
292+
def incl(elem: A): Set[A] =
293+
if (contains(elem)) this
294+
else HashSet.empty[A] + elem1 + elem2 + elem3 + elem4 + elem
295+
def excl(elem: A): Set[A] =
296+
if (elem == elem1) new Set3(elem2, elem3, elem4)
297+
else if (elem == elem2) new Set3(elem1, elem3, elem4)
298+
else if (elem == elem3) new Set3(elem1, elem2, elem4)
299+
else if (elem == elem4) new Set3(elem1, elem2, elem3)
300+
else this
301+
def iterator: Iterator[A] = new SetNIterator[A](size) {
302+
def apply(i: Int) = getElem(i)
303+
}
304+
private def getElem(i: Int) = i match { case 0 => elem1 case 1 => elem2 case 2 => elem3 case 3 => elem4 }
305+
306+
override def foreach[U](f: A => U): Unit = {
307+
f(elem1); f(elem2); f(elem3); f(elem4)
308+
}
309+
override def exists(p: A => Boolean): Boolean = {
310+
p(elem1) || p(elem2) || p(elem3) || p(elem4)
311+
}
312+
override def forall(p: A => Boolean): Boolean = {
313+
p(elem1) && p(elem2) && p(elem3) && p(elem4)
314+
}
315+
override protected[collection] def filterImpl(pred: A => Boolean, isFlipped: Boolean): Set[A] = {
316+
var r1, r2, r3: A = null.asInstanceOf[A]
317+
var n = 0
318+
if (pred(elem1) != isFlipped) { r1 = elem1; n += 1}
319+
if (pred(elem2) != isFlipped) { if (n == 0) r1 = elem2 else r2 = elem2; n += 1}
320+
if (pred(elem3) != isFlipped) { if (n == 0) r1 = elem3 else if (n == 1) r2 = elem3 else r3 = elem3; n += 1}
321+
if (pred(elem4) != isFlipped) { if (n == 0) r1 = elem4 else if (n == 1) r2 = elem4 else if (n == 2) r3 = elem4; n += 1}
322+
323+
n match {
324+
case 0 => Set.empty
325+
case 1 => new Set1(r1)
326+
case 2 => new Set2(r1, r2)
327+
case 3 => new Set3(r1, r2, r3)
328+
case 4 => this
329+
}
330+
}
331+
332+
override def find(p: A => Boolean): Option[A] = {
333+
if (p(elem1)) Some(elem1)
334+
else if (p(elem2)) Some(elem2)
335+
else if (p(elem3)) Some(elem3)
336+
else if (p(elem4)) Some(elem4)
337+
else None
338+
}
339+
override def head: A = elem1
340+
override def tail: Set[A] = new Set3(elem2, elem3, elem4)
341+
342+
private[immutable] def buildTo(builder: Builder[A, Set[A]]): builder.type =
343+
builder.addOne(elem1).addOne(elem2).addOne(elem3).addOne(elem4)
344+
}
345+
}
346+
347+
/** Explicit instantiation of the `Set` trait to reduce class file size in subclasses. */
348+
abstract class AbstractSet[A] extends scala.collection.AbstractSet[A] with Set[A]
349+
350+
/** Builder for Set.
351+
* $multipleResults
352+
*/
353+
private final class SetBuilderImpl[A] extends ReusableBuilder[A, Set[A]] {
354+
private[this] var elems: Set[A] = Set.empty
355+
private[this] var switchedToHashSetBuilder: Boolean = false
356+
private[this] var hashSetBuilder: HashSetBuilder[A] = _
357+
358+
override def clear(): Unit = {
359+
elems = Set.empty
360+
if (hashSetBuilder != null) {
361+
hashSetBuilder.clear()
362+
}
363+
switchedToHashSetBuilder = false
364+
}
365+
366+
override def result(): Set[A] =
367+
if (switchedToHashSetBuilder) hashSetBuilder.result() else elems
368+
369+
def addOne(elem: A) = {
370+
if (switchedToHashSetBuilder) {
371+
hashSetBuilder.addOne(elem)
372+
} else if (elems.size < 4) {
373+
elems = elems + elem
374+
} else {
375+
// assert(elems.size == 4)
376+
if (elems.contains(elem)) {
377+
() // do nothing
378+
} else {
379+
switchedToHashSetBuilder = true
380+
if (hashSetBuilder == null) {
381+
hashSetBuilder = new HashSetBuilder
382+
}
383+
elems.asInstanceOf[Set4[A]].buildTo(hashSetBuilder)
384+
hashSetBuilder.addOne(elem)
385+
}
386+
}
387+
388+
this
389+
}
390+
391+
override def addAll(xs: IterableOnce[A]): this.type =
392+
if (switchedToHashSetBuilder) {
393+
hashSetBuilder.addAll(xs)
394+
this
395+
} else {
396+
super.addAll(xs)
397+
}
398+
}

0 commit comments

Comments
 (0)