Skip to content

Commit 4d26645

Browse files
committed
review comments
add documentation, remove some duplicate code and tighten visibility/naming
1 parent 437085e commit 4d26645

File tree

3 files changed

+88
-55
lines changed

3 files changed

+88
-55
lines changed

library/src/scala/collection/immutable/RedBlackTree.scala

Lines changed: 84 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -509,41 +509,73 @@ private[collection] object NewRedBlackTree {
509509
* works on the current implementation of the Scala compiler).
510510
*
511511
* An alternative is to implement the these classes using plain old Java code...
512+
*
513+
* Mutability
514+
* This implementation encodes both mutable and immutable trees.
515+
* Mutable trees are never exposed to the user code but we get significant reductions in both CPU and allocations
516+
* by maintaining a mutable tree during internal operations, e.g. a builder building a Tree, and the other bulk
517+
* API such as filter or ++
518+
*
519+
* Mutable trees are only used within the confines of this bulk operation and not shared
520+
* Mutable trees may transition to become immutable by calling beforePublish
521+
* Mutable trees may have child nodes (left and right) which are immutable Trees (this promotes structural sharing)
522+
*
523+
* Immutable trees may only child nodes (left and right) which are immutable Trees, and as such the immutable
524+
* trees the entire transitive subtree is immutable
525+
*
526+
* Colour, mutablity and size encoding
527+
* The colour of the Tree, its mutablity and size are all encoded in the _count field
528+
* The colour is encoded in the top bit (31) of the _count. This allows a mutable tree to change colour without
529+
* additional allocation
530+
* The mutable trees always have bits 0 .. 30 (inclusive) set to 0
531+
* The immutable trees always have bits 0 .. 30 containing the size of the transitive subtree
532+
*
533+
* Naming
534+
* All of the methods that can yield a mutable result have "mutable" on their name, and generally there
535+
* is another method similarly named with doesn't. This is to aid safety and to reduce the cognitive load when
536+
* reviewing changes. e.g.
537+
* def upd(...) will update an immutable Tree, producing an immutable Tree
538+
* def mutableUpd(...) will update a mutable or immutable Tree and may return a mutable or immutable Tree
539+
* a method that has mutable in its name may return a immutable tree if the operation can reuse the existing tree
540+
*
512541
*/
513-
final class Tree[A, +B](
542+
private[immutable] final class Tree[A, +B](
514543
@(inline @getter @setter) private var _key: A,
515544
@(inline @getter @setter) private var _value: AnyRef,
516545
@(inline @getter @setter) private var _left: Tree[A, _],
517546
@(inline @getter @setter) private var _right: Tree[A, _],
518547
@(inline @getter @setter) private var _count: Int)
519548
{
520-
def isMutable: Boolean = (_count & 0x7FFFFFFF) == 0
549+
private[NewRedBlackTree] def isMutable: Boolean = (_count & 0x7FFFFFFF) == 0
521550
// read only APIs
522-
@`inline` final def count = {
551+
@`inline` private[NewRedBlackTree] final def count = {
523552
//devTimeAssert((_count & 0x7FFFFFFF) != 0)
524553
_count & 0x7FFFFFFF
525554
}
526555
//inlined here to avoid outer object null checks
527-
@`inline` final def sizeOf(tree:Tree[_,_]) = if (tree eq null) 0 else tree.count
528-
@`inline` final def key = _key
529-
@`inline` final def value = _value.asInstanceOf[B]
530-
@`inline` final def left = _left.asInstanceOf[Tree[A, B]]
531-
@`inline` final def right = _right.asInstanceOf[Tree[A, B]]
532-
@`inline` final def isBlack = _count < 0
533-
@`inline` final def isRed = _count >= 0
556+
@`inline` private[NewRedBlackTree] final def sizeOf(tree:Tree[_,_]) = if (tree eq null) 0 else tree.count
557+
@`inline` private[immutable] final def key = _key
558+
@`inline` private[immutable] final def value = _value.asInstanceOf[B]
559+
//Note - in 2.13 this should be private[RedBlackTree] as its only needed or the 2.12 Old RedBlackTree
560+
@`inline` private[immutable] final def left = _left.asInstanceOf[Tree[A, B]]
561+
//Note - in 2.13 this should be private[RedBlackTree] as its only needed or the 2.12 Old RedBlackTree
562+
@`inline` private[immutable] final def right = _right.asInstanceOf[Tree[A, B]]
563+
//Note - only used in tests outside RedBlackTree
564+
@`inline` private[immutable] final def isBlack = _count < 0
565+
//Note - only used in tests outside RedBlackTree
566+
@`inline` private[immutable] final def isRed = _count >= 0
534567

535568
override def toString: String = s"${if(isRed) "RedTree" else "BlackTree"}($key, $value, $left, $right)"
536569

537570
@`inline` private def initialCount = _count & 0x80000000
538-
@`inline` def colourBit = 0x80000000
539-
@`inline` def initialBlackCount = 0x80000000
540-
@`inline` def initialRedCount = 0
571+
@`inline` private[NewRedBlackTree] def colourBit = 0x80000000
572+
@`inline` private[NewRedBlackTree] def initialBlackCount = 0x80000000
573+
@`inline` private[NewRedBlackTree] def initialRedCount = 0
541574

542575
//mutable APIs
543-
@`inline` def mutable = (_count & 0x7FFFFFFF) == 0
544-
def makeImmutable: Tree[A, B] = {
576+
private[NewRedBlackTree] def makeImmutable: Tree[A, B] = {
545577
def makeImmutableImpl() = {
546-
if (mutable) {
578+
if (isMutable) {
547579
var size = 1
548580
if (_left ne null) {
549581
_left.makeImmutable
@@ -561,15 +593,15 @@ private[collection] object NewRedBlackTree {
561593
this
562594
}
563595

564-
def mutableBlack: Tree[A, B] = {
596+
private[NewRedBlackTree] def mutableBlack: Tree[A, B] = {
565597
if (isBlack) this
566-
else if (mutable) {
598+
else if (isMutable) {
567599
_count = initialBlackCount
568600
this
569601
}
570602
else new Tree(_key, _value, _left, _right, initialBlackCount)
571603
}
572-
// def mutableRed: Tree[A, B] = {
604+
// private[NewRedBlackTree] def mutableRed: Tree[A, B] = {
573605
// if (isRed) this
574606
// else if (mutable) {
575607
// _count = initialRedCount
@@ -579,93 +611,93 @@ private[collection] object NewRedBlackTree {
579611
// }
580612
//Note - in 2.13 remove his method
581613
//due to the handling of keys in 2.13 we never replace a key
582-
def mutableWithK[B1 >: B](newKey: A): Tree[A, B1] = {
614+
private[NewRedBlackTree] def mutableWithK[B1 >: B](newKey: A): Tree[A, B1] = {
583615
if (newKey.asInstanceOf[AnyRef] eq _key.asInstanceOf[AnyRef]) this
584-
else if (mutable) {
616+
else if (isMutable) {
585617
_key = newKey
586618
this
587619
} else new Tree(newKey, _value.asInstanceOf[AnyRef], _left, _right, initialCount)
588620
}
589-
def mutableWithV[B1 >: B](newValue: B1): Tree[A, B1] = {
621+
private[NewRedBlackTree] def mutableWithV[B1 >: B](newValue: B1): Tree[A, B1] = {
590622
if (newValue.asInstanceOf[AnyRef] eq _value.asInstanceOf[AnyRef]) this
591-
else if (mutable) {
623+
else if (isMutable) {
592624
_value = newValue.asInstanceOf[AnyRef]
593625
this
594626
} else new Tree(_key, newValue.asInstanceOf[AnyRef], _left, _right, initialCount)
595627
}
596628
//Note - in 2.13 remove his method
597629
//due to the handling of keys in 2.13 we never replace a key
598-
def mutableWithKV[B1 >: B](newKey: A, newValue: B1): Tree[A, B1] = {
630+
private[NewRedBlackTree] def mutableWithKV[B1 >: B](newKey: A, newValue: B1): Tree[A, B1] = {
599631
if ((newKey.asInstanceOf[AnyRef] eq _key.asInstanceOf[AnyRef]) &&
600632
(newValue.asInstanceOf[AnyRef] eq _value.asInstanceOf[AnyRef])) this
601-
else if (mutable) {
633+
else if (isMutable) {
602634
_key = newKey
603635
_value = newValue.asInstanceOf[AnyRef]
604636
this
605637
} else new Tree(newKey, newValue.asInstanceOf[AnyRef], _left, _right, initialCount)
606638
}
607-
def mutableWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = {
639+
private[NewRedBlackTree] def mutableWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = {
608640
if (_left eq newLeft) this
609-
else if (mutable) {
641+
else if (isMutable) {
610642
_left = newLeft
611643
this
612644
} else new Tree(_key, _value, newLeft, _right, initialCount)
613645
}
614-
def mutableWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = {
646+
private[NewRedBlackTree] def mutableWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = {
615647
if (_right eq newRight) this
616-
else if (mutable) {
648+
else if (isMutable) {
617649
_right = newRight
618650
this
619651
} else new Tree(_key, _value, _left, newRight, initialCount)
620652
}
621-
def mutableWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = {
653+
private[NewRedBlackTree] def mutableWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = {
622654
if ((_left eq newLeft) && (_right eq newRight)) this
623-
else if (mutable) {
655+
else if (isMutable) {
624656
_left = newLeft
625657
_right = newRight
626658
this
627659
} else new Tree(_key, _value, newLeft, newRight, initialCount)
628660
}
629-
def mutableBlackWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = {
661+
private[NewRedBlackTree] def mutableBlackWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = {
630662
if ((_left eq newLeft) && isBlack) this
631-
else if (mutable) {
663+
else if (isMutable) {
632664
_count = initialBlackCount
633665
_left = newLeft
634666
this
635667
} else new Tree(_key, _value, newLeft, _right, initialBlackCount)
636668
}
637-
def mutableBlackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = {
669+
private[NewRedBlackTree] def mutableBlackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = {
638670
if ((_right eq newRight) && isBlack) this
639-
else if (mutable) {
671+
else if (isMutable) {
640672
_count = initialBlackCount
641673
_right = newRight
642674
this
643675
} else new Tree(_key, _value, _left, newRight, initialBlackCount)
644676
}
645677

646-
def black: Tree[A, B] = {
678+
private[NewRedBlackTree] def black: Tree[A, B] = {
647679
//assertNotMutable(this)
648680
if (isBlack) this
649681
else new Tree(_key, _value, _left, _right, _count ^ colourBit)
650682
}
651-
def red: Tree[A, B] = {
683+
private[NewRedBlackTree] def red: Tree[A, B] = {
652684
//assertNotMutable(this)
653685
if (isRed) this
654686
else new Tree(_key, _value, _left, _right, _count ^ colourBit)
655687
}
656-
def withKV[B1 >: B](newKey: A, newValue: B1): Tree[A, B1] = {
688+
private[NewRedBlackTree] def withKV[B1 >: B](newKey: A, newValue: B1): Tree[A, B1] = {
657689
//assertNotMutable(this)
658690
if ((newKey.asInstanceOf[AnyRef] eq _key.asInstanceOf[AnyRef]) &&
659691
(newValue.asInstanceOf[AnyRef] eq _value.asInstanceOf[AnyRef])) this
660692
else new Tree(newKey, newValue.asInstanceOf[AnyRef], _left, _right, _count)
661693
}
662-
def withV[B1 >: B](newValue: B1): Tree[A, B1] = {
694+
private[NewRedBlackTree] def withV[B1 >: B](newValue: B1): Tree[A, B1] = {
663695
//assertNotMutable(this)
664696
if (newValue.asInstanceOf[AnyRef] eq _value.asInstanceOf[AnyRef]) this
665697
else new Tree(_key, newValue.asInstanceOf[AnyRef], _left, _right, _count)
666698
}
667699

668-
def withLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = {
700+
private[NewRedBlackTree] def withLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = {
669701
//assertNotMutable(this)
670702
//assertNotMutable(newLeft)
671703
if (newLeft eq _left) this
@@ -674,7 +706,7 @@ private[collection] object NewRedBlackTree {
674706
new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, (_count & colourBit) | size)
675707
}
676708
}
677-
def withRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = {
709+
private[NewRedBlackTree] def withRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = {
678710
//assertNotMutable(this)
679711
//assertNotMutable(newRight)
680712
if (newRight eq _right) this
@@ -683,7 +715,7 @@ private[collection] object NewRedBlackTree {
683715
new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, (_count & colourBit) | size)
684716
}
685717
}
686-
def blackWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = {
718+
private[NewRedBlackTree] def blackWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = {
687719
//assertNotMutable(this)
688720
//assertNotMutable(newLeft)
689721
if ((newLeft eq _left) && isBlack) this
@@ -692,7 +724,7 @@ private[collection] object NewRedBlackTree {
692724
new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialBlackCount | size)
693725
}
694726
}
695-
def blackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = {
727+
private[NewRedBlackTree] def blackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = {
696728
//assertNotMutable(this)
697729
//assertNotMutable(newRight)
698730
if ((newRight eq _right) && isBlack) this
@@ -701,7 +733,7 @@ private[collection] object NewRedBlackTree {
701733
new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialBlackCount | size)
702734
}
703735
}
704-
def withLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = {
736+
private[NewRedBlackTree] def withLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = {
705737
//assertNotMutable(this)
706738
//assertNotMutable(newLeft)
707739
//assertNotMutable(newRight)
@@ -712,22 +744,24 @@ private[collection] object NewRedBlackTree {
712744
}
713745
}
714746
}
715-
@`inline` def initialBlackCount = 0x80000000
716-
@`inline` def initialRedCount = 0
747+
@`inline` private[NewRedBlackTree] def initialBlackCount = 0x80000000
748+
@`inline` private[NewRedBlackTree] def initialRedCount = 0
717749

718-
@`inline` def mutableRedTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialRedCount)
719-
@`inline` def mutableBlackTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialBlackCount)
750+
@`inline` private[NewRedBlackTree] def mutableRedTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialRedCount)
751+
@`inline` private[NewRedBlackTree] def mutableBlackTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = new Tree[A,B](key, value.asInstanceOf[AnyRef], left, right, initialBlackCount)
720752

721753
/** create a new immutable red tree.
722754
* left and right may be null
723755
*/
724-
def RedTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]): Tree[A, B] = {
756+
//Note - in 2.13 this should be private[RedBlackTree] as its only needed or the 2.12 Old RedBlackTree
757+
private[immutable] def RedTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]): Tree[A, B] = {
725758
//assertNotMutable(left)
726759
//assertNotMutable(right)
727760
val size = sizeOf(left) + sizeOf(right) + 1
728761
new Tree(key, value.asInstanceOf[AnyRef], left, right, initialRedCount | size)
729762
}
730-
def BlackTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]): Tree[A, B] = {
763+
//Note - in 2.13 this should be private[RedBlackTree] as its only needed or the 2.12 Old RedBlackTree
764+
private[immutable] def BlackTree[A, B](key: A, value: B, left: Tree[A, B], right: Tree[A, B]): Tree[A, B] = {
731765
//assertNotMutable(left)
732766
//assertNotMutable(right)
733767
val size = sizeOf(left) + sizeOf(right) + 1

library/src/scala/collection/immutable/TreeMap.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,12 @@ object TreeMap extends ImmutableSortedMapFactory[TreeMap] {
4848
// we cache tree to avoid the outer access to tree
4949
// in the hot path (apply)
5050
private[this] var accumulator :Tree = null
51-
def addForEach(aTree:Tree, hasForEach: HasForeachEntry[A, B]): Tree = {
51+
def addForEach(hasForEach: HasForeachEntry[A, B]): Unit = {
5252
accumulator = tree
5353
hasForEach.foreachEntry(this)
54-
val result = accumulator
54+
tree = accumulator
5555
// be friendly to GC
5656
accumulator = null
57-
result
5857
}
5958

6059
override def apply(key: A, value: B): Unit = {
@@ -73,7 +72,7 @@ object TreeMap extends ImmutableSortedMapFactory[TreeMap] {
7372
else tree = RB.union(beforePublish(tree), ts.tree0)
7473
case that: HasForeachEntry[A, B] =>
7574
//add avoiding creation of tuples
76-
tree = adder.addForEach(tree, that)
75+
adder.addForEach(that)
7776
case _ =>
7877
super.++=(xs)
7978
}

library/src/scala/collection/immutable/TreeSet.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[
277277

278278
private [collection] def removeAll(xs : GenTraversableOnce[A]): TreeSet[A] = xs match {
279279
case ts: TreeSet[A] if ordering == ts.ordering =>
280-
newSetOrSelf(RB.difference(tree, ts.tree))
280+
newSetOrSelf(RB.difference(tree, ts.tree))
281281
case _ =>
282282
//TODO add an implementation of a mutable subtractor similar to TreeMap
283283
//but at least this doesn't create a TreeSet for each iteration

0 commit comments

Comments
 (0)