Skip to content

Commit 2f7a622

Browse files
committed
Merge pull request scala#4608 from ruippeixotog/improve-mutable-treeset
SI-6938 Use mutable red-black tree in `mutable.TreeSet`
2 parents 466821d + c152590 commit 2f7a622

File tree

8 files changed

+424
-59
lines changed

8 files changed

+424
-59
lines changed

src/library/scala/collection/mutable/RedBlackTree.scala

+33
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ private[collection] object RedBlackTree {
8282
case node => Some((node.key, node.value))
8383
}
8484

85+
def minKey[A](tree: Tree[A, _]): Option[A] = minNode(tree.root) match {
86+
case null => None
87+
case node => Some(node.key)
88+
}
89+
8590
private def minNode[A, B](node: Node[A, B]): Node[A, B] =
8691
if (node eq null) null else minNodeNonNull(node)
8792

@@ -93,6 +98,11 @@ private[collection] object RedBlackTree {
9398
case node => Some((node.key, node.value))
9499
}
95100

101+
def maxKey[A](tree: Tree[A, _]): Option[A] = maxNode(tree.root) match {
102+
case null => None
103+
case node => Some(node.key)
104+
}
105+
96106
private def maxNode[A, B](node: Node[A, B]): Node[A, B] =
97107
if (node eq null) null else maxNodeNonNull(node)
98108

@@ -109,6 +119,12 @@ private[collection] object RedBlackTree {
109119
case node => Some((node.key, node.value))
110120
}
111121

122+
def minKeyAfter[A](tree: Tree[A, _], key: A)(implicit ord: Ordering[A]): Option[A] =
123+
minNodeAfter(tree.root, key) match {
124+
case null => None
125+
case node => Some(node.key)
126+
}
127+
112128
private[this] def minNodeAfter[A, B](node: Node[A, B], key: A)(implicit ord: Ordering[A]): Node[A, B] = {
113129
if (node eq null) null
114130
else {
@@ -133,6 +149,12 @@ private[collection] object RedBlackTree {
133149
case node => Some((node.key, node.value))
134150
}
135151

152+
def maxKeyBefore[A](tree: Tree[A, _], key: A)(implicit ord: Ordering[A]): Option[A] =
153+
maxNodeBefore(tree.root, key) match {
154+
case null => None
155+
case node => Some(node.key)
156+
}
157+
136158
private[this] def maxNodeBefore[A, B](node: Node[A, B], key: A)(implicit ord: Ordering[A]): Node[A, B] = {
137159
if (node eq null) null
138160
else {
@@ -411,6 +433,17 @@ private[collection] object RedBlackTree {
411433
if (node.right ne null) foreachNodeNonNull(node.right, f)
412434
}
413435

436+
def foreachKey[A, U](tree: Tree[A, _], f: A => U): Unit = foreachNodeKey(tree.root, f)
437+
438+
private[this] def foreachNodeKey[A, U](node: Node[A, _], f: A => U): Unit =
439+
if (node ne null) foreachNodeKeyNonNull(node, f)
440+
441+
private[this] def foreachNodeKeyNonNull[A, U](node: Node[A, _], f: A => U): Unit = {
442+
if (node.left ne null) foreachNodeKeyNonNull(node.left, f)
443+
f(node.key)
444+
if (node.right ne null) foreachNodeKeyNonNull(node.right, f)
445+
}
446+
414447
def transform[A, B](tree: Tree[A, B], f: (A, B) => B): Unit = transformNode(tree.root, f)
415448

416449
private[this] def transformNode[A, B, U](node: Node[A, B], f: (A, B) => B): Unit =

src/library/scala/collection/mutable/SortedSet.scala

+3
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@ object SortedSet extends MutableSortedSetFactory[SortedSet] {
4848
def empty[A](implicit ord: Ordering[A]): SortedSet[A] = TreeSet.empty[A]
4949

5050
}
51+
52+
/** Explicit instantiation of the `SortedSet` trait to reduce class file size in subclasses. */
53+
abstract class AbstractSortedSet[A] extends scala.collection.mutable.AbstractSet[A] with SortedSet[A]

src/library/scala/collection/mutable/TreeMap.scala

+19
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ sealed class TreeMap[A, B] private (tree: RB.Tree[A, B])(implicit val ordering:
5252
override def empty = TreeMap.empty
5353
override protected[this] def newBuilder = TreeMap.newBuilder[A, B]
5454

55+
/**
56+
* Creates a ranged projection of this map. Any mutations in the ranged projection will update the original map and
57+
* vice versa.
58+
*
59+
* Only entries with keys between this projection's key range will ever appear as elements of this map, independently
60+
* of whether the entries are added through the original map or through this view. That means that if one inserts a
61+
* key-value in a view whose key is outside the view's bounds, calls to `get` or `contains` will _not_ consider the
62+
* newly added entry. Mutations are always reflected in the original map, though.
63+
*
64+
* @param from the lower bound (inclusive) of this projection wrapped in a `Some`, or `None` if there is no lower
65+
* bound.
66+
* @param until the upper bound (exclusive) of this projection wrapped in a `Some`, or `None` if there is no upper
67+
* bound.
68+
*/
5569
def rangeImpl(from: Option[A], until: Option[A]): TreeMap[A, B] = new TreeMapView(from, until)
5670

5771
def -=(key: A): this.type = { RB.delete(tree, key); this }
@@ -157,10 +171,15 @@ sealed class TreeMap[A, B] private (tree: RB.Tree[A, B])(implicit val ordering:
157171
}
158172
}
159173

174+
// Using the iterator should be efficient enough; if performance is deemed a problem later, specialized
175+
// `foreach(f, from, until)` and `transform(f, from, until)` methods can be created in `RedBlackTree`. See
176+
// https://github.com/scala/scala/pull/4608#discussion_r34307985 for a discussion about this.
160177
override def foreach[U](f: ((A, B)) => U): Unit = iterator.foreach(f)
161178
override def transform(f: (A, B) => B) = {
162179
iterator.foreach { case (key, value) => update(key, f(key, value)) }
163180
this
164181
}
182+
183+
override def clone() = super.clone().rangeImpl(from, until)
165184
}
166185
}

src/library/scala/collection/mutable/TreeSet.scala

+127-54
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ package collection
1111
package mutable
1212

1313
import generic._
14-
import scala.collection.immutable.{RedBlackTree => RB}
15-
import scala.runtime.ObjectRef
14+
import scala.collection.mutable.{RedBlackTree => RB}
1615

1716
/**
1817
* @define Coll `mutable.TreeSet`
@@ -29,88 +28,162 @@ object TreeSet extends MutableSortedSetFactory[TreeSet] {
2928
*/
3029
def empty[A](implicit ordering: Ordering[A]) = new TreeSet[A]()
3130

31+
/** $sortedMapCanBuildFromInfo */
32+
implicit def canBuildFrom[A](implicit ord: Ordering[A]): CanBuildFrom[Coll, A, TreeSet[A]] =
33+
new SortedSetCanBuildFrom[A]
3234
}
3335

3436
/**
35-
* A mutable SortedSet using an immutable RedBlack Tree as underlying data structure.
37+
* A mutable sorted set implemented using a mutable red-black tree as underlying data structure.
3638
*
37-
* @author Lucien Pereira
39+
* @param ordering the implicit ordering used to compare objects of type `A`.
40+
* @tparam A the type of the keys contained in this tree set.
41+
*
42+
* @author Rui Gonçalves
43+
* @version 2.12
44+
* @since 2.10
3845
*
46+
* @define Coll mutable.TreeSet
47+
* @define coll mutable tree set
3948
*/
40-
@deprecatedInheritance("TreeSet is not designed to enable meaningful subclassing.", "2.11.0")
41-
class TreeSet[A] private (treeRef: ObjectRef[RB.Tree[A, Null]], from: Option[A], until: Option[A])(implicit val ordering: Ordering[A])
42-
extends SortedSet[A] with SetLike[A, TreeSet[A]]
43-
with SortedSetLike[A, TreeSet[A]] with Set[A] with Serializable {
49+
// Original API designed in part by Lucien Pereira
50+
@SerialVersionUID(-3642111301929493640L)
51+
sealed class TreeSet[A] private (tree: RB.Tree[A, Null])(implicit val ordering: Ordering[A])
52+
extends AbstractSortedSet[A]
53+
with SortedSet[A]
54+
with SetLike[A, TreeSet[A]]
55+
with SortedSetLike[A, TreeSet[A]]
56+
with Serializable {
4457

4558
if (ordering eq null)
4659
throw new NullPointerException("ordering must not be null")
4760

48-
def this()(implicit ordering: Ordering[A]) = this(new ObjectRef(null), None, None)
61+
/**
62+
* Creates an empty `TreeSet`.
63+
* @param ord the implicit ordering used to compare objects of type `A`.
64+
* @return an empty `TreeSet`.
65+
*/
66+
def this()(implicit ord: Ordering[A]) = this(RB.Tree.empty)(ord)
4967

50-
override def size: Int = RB.countInRange(treeRef.elem, from, until)
68+
override def empty = TreeSet.empty
69+
override protected[this] def newBuilder = TreeSet.newBuilder[A]
5170

52-
override def stringPrefix = "TreeSet"
71+
/**
72+
* Creates a ranged projection of this set. Any mutations in the ranged projection affect will update the original set
73+
* and vice versa.
74+
*
75+
* Only keys between this projection's key range will ever appear as elements of this set, independently of whether
76+
* the elements are added through the original set or through this view. That means that if one inserts an element in
77+
* a view whose key is outside the view's bounds, calls to `contains` will _not_ consider the newly added element.
78+
* Mutations are always reflected in the original set, though.
79+
*
80+
* @param from the lower bound (inclusive) of this projection wrapped in a `Some`, or `None` if there is no lower
81+
* bound.
82+
* @param until the upper bound (exclusive) of this projection wrapped in a `Some`, or `None` if there is no upper
83+
* bound.
84+
*/
85+
def rangeImpl(from: Option[A], until: Option[A]): TreeSet[A] = new TreeSetView(from, until)
5386

54-
override def empty: TreeSet[A] = TreeSet.empty
87+
def -=(key: A): this.type = { RB.delete(tree, key); this }
88+
def +=(elem: A): this.type = { RB.insert(tree, elem, null); this }
5589

56-
private def pickBound(comparison: (A, A) => A, oldBound: Option[A], newBound: Option[A]) = (newBound, oldBound) match {
57-
case (Some(newB), Some(oldB)) => Some(comparison(newB, oldB))
58-
case (None, _) => oldBound
59-
case _ => newBound
60-
}
90+
def contains(elem: A) = RB.contains(tree, elem)
6191

62-
override def rangeImpl(fromArg: Option[A], untilArg: Option[A]): TreeSet[A] = {
63-
val newFrom = pickBound(ordering.max, fromArg, from)
64-
val newUntil = pickBound(ordering.min, untilArg, until)
92+
def iterator = RB.keysIterator(tree)
93+
def keysIteratorFrom(start: A) = RB.keysIterator(tree, Some(start))
94+
override def iteratorFrom(start: A) = RB.keysIterator(tree, Some(start))
6595

66-
new TreeSet(treeRef, newFrom, newUntil)
67-
}
96+
override def size = RB.size(tree)
97+
override def isEmpty = RB.isEmpty(tree)
6898

69-
override def -=(elem: A): this.type = {
70-
treeRef.elem = RB.delete(treeRef.elem, elem)
71-
this
72-
}
99+
override def head = RB.minKey(tree).get
100+
override def headOption = RB.minKey(tree)
101+
override def last = RB.maxKey(tree).get
102+
override def lastOption = RB.maxKey(tree)
73103

74-
override def +=(elem: A): this.type = {
75-
treeRef.elem = RB.update(treeRef.elem, elem, null, overwrite = false)
76-
this
77-
}
104+
override def foreach[U](f: A => U): Unit = RB.foreachKey(tree, f)
105+
override def clear(): Unit = RB.clear(tree)
106+
107+
override def stringPrefix = "TreeSet"
78108

79109
/**
80-
* Thanks to the immutable nature of the
81-
* underlying Tree, we can share it with
82-
* the clone. So clone complexity in time is O(1).
110+
* A ranged projection of a [[TreeSet]]. Mutations on this set affect the original set and vice versa.
83111
*
112+
* Only keys between this projection's key range will ever appear as elements of this set, independently of whether
113+
* the elements are added through the original set or through this view. That means that if one inserts an element in
114+
* a view whose key is outside the view's bounds, calls to `contains` will _not_ consider the newly added element.
115+
* Mutations are always reflected in the original set, though.
116+
*
117+
* @param from the lower bound (inclusive) of this projection wrapped in a `Some`, or `None` if there is no lower
118+
* bound.
119+
* @param until the upper bound (exclusive) of this projection wrapped in a `Some`, or `None` if there is no upper
120+
* bound.
84121
*/
85-
override def clone(): TreeSet[A] =
86-
new TreeSet[A](new ObjectRef(treeRef.elem), from, until)
87-
88-
private val notProjection = !(from.isDefined || until.isDefined)
122+
@SerialVersionUID(7087824939194006086L)
123+
private[this] final class TreeSetView(from: Option[A], until: Option[A]) extends TreeSet[A](tree) {
124+
125+
/**
126+
* Given a possible new lower bound, chooses and returns the most constraining one (the maximum).
127+
*/
128+
private[this] def pickLowerBound(newFrom: Option[A]): Option[A] = (from, newFrom) match {
129+
case (Some(fr), Some(newFr)) => Some(ordering.max(fr, newFr))
130+
case (None, _) => newFrom
131+
case _ => from
132+
}
89133

90-
override def contains(elem: A): Boolean = {
91-
def leftAcceptable: Boolean = from match {
92-
case Some(lb) => ordering.gteq(elem, lb)
93-
case _ => true
134+
/**
135+
* Given a possible new upper bound, chooses and returns the most constraining one (the minimum).
136+
*/
137+
private[this] def pickUpperBound(newUntil: Option[A]): Option[A] = (until, newUntil) match {
138+
case (Some(unt), Some(newUnt)) => Some(ordering.min(unt, newUnt))
139+
case (None, _) => newUntil
140+
case _ => until
94141
}
95142

96-
def rightAcceptable: Boolean = until match {
97-
case Some(ub) => ordering.lt(elem, ub)
98-
case _ => true
143+
/**
144+
* Returns true if the argument is inside the view bounds (between `from` and `until`).
145+
*/
146+
private[this] def isInsideViewBounds(key: A): Boolean = {
147+
val afterFrom = from.isEmpty || ordering.compare(from.get, key) <= 0
148+
val beforeUntil = until.isEmpty || ordering.compare(key, until.get) < 0
149+
afterFrom && beforeUntil
99150
}
100151

101-
(notProjection || (leftAcceptable && rightAcceptable)) &&
102-
RB.contains(treeRef.elem, elem)
103-
}
152+
override def rangeImpl(from: Option[A], until: Option[A]): TreeSet[A] =
153+
new TreeSetView(pickLowerBound(from), pickUpperBound(until))
154+
155+
override def contains(key: A) = isInsideViewBounds(key) && RB.contains(tree, key)
156+
157+
override def iterator = RB.keysIterator(tree, from, until)
158+
override def keysIteratorFrom(start: A) = RB.keysIterator(tree, pickLowerBound(Some(start)), until)
159+
override def iteratorFrom(start: A) = RB.keysIterator(tree, pickLowerBound(Some(start)), until)
104160

105-
override def iterator: Iterator[A] = iteratorFrom(None)
161+
override def size = iterator.length
162+
override def isEmpty = !iterator.hasNext
106163

107-
override def keysIteratorFrom(start: A) = iteratorFrom(Some(start))
164+
override def head = headOption.get
165+
override def headOption = {
166+
val elem = if (from.isDefined) RB.minKeyAfter(tree, from.get) else RB.minKey(tree)
167+
(elem, until) match {
168+
case (Some(e), Some(unt)) if ordering.compare(e, unt) >= 0 => None
169+
case _ => elem
170+
}
171+
}
108172

109-
private def iteratorFrom(start: Option[A]) = {
110-
val it = RB.keysIterator(treeRef.elem, pickBound(ordering.max, from, start))
111-
until match {
112-
case None => it
113-
case Some(ub) => it takeWhile (k => ordering.lt(k, ub))
173+
override def last = lastOption.get
174+
override def lastOption = {
175+
val elem = if (until.isDefined) RB.maxKeyBefore(tree, until.get) else RB.maxKey(tree)
176+
(elem, from) match {
177+
case (Some(e), Some(fr)) if ordering.compare(e, fr) < 0 => None
178+
case _ => elem
179+
}
114180
}
181+
182+
// Using the iterator should be efficient enough; if performance is deemed a problem later, a specialized
183+
// `foreachKey(f, from, until)` method can be created in `RedBlackTree`. See
184+
// https://github.com/scala/scala/pull/4608#discussion_r34307985 for a discussion about this.
185+
override def foreach[U](f: A => U): Unit = iterator.foreach(f)
186+
187+
override def clone() = super.clone().rangeImpl(from, until)
115188
}
116189
}

0 commit comments

Comments
 (0)